LINUX.ORG.RU

Очистить состояние процесса без execve()

 


0

1

Допустим, у нас есть привилегированный процесс, и он хочет сделать что-то в контексте каких-то непривилегированных прав (и там и остаться). Для этого он переключает себе uid/gid или лезет в контейнер, не важно.

Тут нас подстерегает опасность: у привилегированного процесса в памяти могут находиться привилегированные данные (те, которые не положено знать обычным процессам), в том числе оставшиеся от когда-то давно free()-d блоков памяти (про explicit_bzero() и подобное тут не будем, это другая тема). Проблема в том, что как только процесс поставит себе права обычного юзера, этот самый обычный юзер может поймать его отладчиком и сдампить память.

Если у нас скрипт и переключаемся на юзера с помощью su, то такой проблемы не возникает: сначала скрипт (форкаясь) делает execve() в su, тот уже с чистой от важных данных памятью делает setuid() и прочее, и потом запускает полезную нагрузку непривилегированного юзера. Но неужели без execve() это никак не сделать? У него есть проблема: ему нужен бинарник в доступном на данный момент файловом пространстве, а если мы например в chroot-е, то там нашего бинарника уже может не быть, либо мы не можем полагаться на его подлинность. Как быть?

Лучше всего подошло бы что-то типа execve-сам-себя, но без указания на файл бинарника (через /proc/self/exe тоже нельзя, потому что в chroot-е его тоже может не быть). Если при этом ещё и можно было передать не только набор текстовых строк, а указать диапазон адресов памяти которые не надо чистить, и точку входа, отличную от main() - вообще прекрасно.

★★★★★

Последнее исправление: firkax (всего исправлений: 1)

Подписался на ответы, хотя сдаётся мне, что «мсье большой оригинал» (c). :)

указать диапазон адресов памяти которые не надо чистить

Ну уж это ты совсем загнул, поскольку (1) PIC; (2) вдруг тебе приспичит несколько раз сделать malloc(1GB), и указать диапазон адресов внутри последнего выделенного блока.

pr849
()
Ответ на: комментарий от pr849

Не вижу проблемы ни в PIC ни в диапазоне из последнего malloc'а (если он выровнен по границам страниц). Да и пусть даже будет ограничение что он должен быть выделен через mmap() (чтобы не думать над тем как он будет взаимодействовать с sbrk в будущем процессе).

firkax ★★★★★
() автор топика

разве через sudo не получится? там то наверно продумали этот вопрос, опятьже процесс привелегированный

anonymous2 ★★★★★
()
Последнее исправление: anonymous2 (всего исправлений: 4)

провел тест, через gdb dump binary memory [heap] для родителя и потомка, то что они разные по размеру и приватных для себя данных не увидел, ядро старое 4.14

anonymous2 ★★★★★
()
Последнее исправление: anonymous2 (всего исправлений: 1)
Ответ на: комментарий от anonymous2

sudo тут ни при чём. В скриптах проблемы нет, я ж написал уже почему. Вопрос про её решение без запуска доп. прог в рамках одного запущеного бинарника.

firkax ★★★★★
() автор топика
Ответ на: комментарий от firkax

почему не клал? но это уже не важно.

чисто практически кто мешает секретные данные чистить после fork до setuid? вера что за вас это сделает linux?

anonymous2 ★★★★★
()
Ответ на: комментарий от anonymous2

Мешает то что ввиду сложности работы как компиляторов так и различных библиотек, а так же кода собственно программы, ты не можешь гарантированно знать в каких байтах памяти могло что-то от них записаться и остаться. А секретны по-хорошему все данные о работе рутовых процессов, кроме тех, которые эти процессы сами кому-то сообщают через свои апи, и тех, которые статически прописаны в общедоступных бинарниках.

Но тема не об этом, прочти наконец её первое сообщение (ну или можешь не читать) и хватит флудить всякими глупостями.

firkax ★★★★★
() автор топика
Ответ на: комментарий от firkax

проблема надумана, нехотите чистить мусор - запускайте под системным пользователем у которого есть права делать что вам нужно в контексте не доверенного пользователя

anonymous2 ★★★★★
()
Последнее исправление: anonymous2 (всего исправлений: 3)

Коротко - никак. Форк был спроектирован для другого. Но можно посоветовать форкаться заранее, до того как в памяти появятся чувствительные данные. Заслипать свою молодую копию, и потом размножать уже её, по необходимости.

raspopov
()
Ответ на: комментарий от raspopov

Ух ты, как хитро, я б не догадался. Хотя это и выглядит костыльно, и есть очевидный минус что эту копию надо постоянно держать параллельно себе (и при «нырянии» в сомнительные контексты там опять делать ещё одну копию если придётся повторить процедуру), но зато вроде бы решает все остальные проблемы. И данные этой копии передать можно любым способом на выбор, включая бесплатное shared memory. Только надо ей на старте ещё почистить аргументы и env. Ну а с дескрипторами (а ещё терминалом и cwd) уже штатная процедура по избавлению от ненужных в момент выполнения задачи.

Ну, с другой стороны, это можно считать закешированным бинарником для запуска.

firkax ★★★★★
() автор топика
27 августа 2024 г.
Ответ на: комментарий от raspopov

Кажется я всё-таки нашёл способ, и он довольно на поверхности. Есть такое fexecve(), которым можно запускать бинарник по его открытому дескриптору, а не по имени файла. Придётся таскать с собой этот самый дескриптор везде, но вроде (если его не открывать с правом на запись) вреда от его утекания в злонамеренный контекст никакого не будет. Есть ограничения:

1) не работает на старых системах

Если Linux то нужно Linux 3.19+ и glibc 2.27+ (с более ранними его либо не будет, либо под этим названием будет неприемлемый костыль через /proc). Если FreeBSD то 8.0+. Другие системы не смотрел.

2) для скриптов не годится (там опять костыль с /proc только уже неисправимый), но это пофиг, речь же про бинарник

3) видимо гарантированную исправную работу можно получить только если бинарник статический, иначе в каком-нить chroot-е можно не найти нужные библиотеки, или найти не те, или вообще не найти динамический загрузчик если например хост 64 бита и контекст куда мы попали - 32.

Думаю это проще чем таскать с собой чистый форк везде.

firkax ★★★★★
() автор топика
Последнее исправление: firkax (всего исправлений: 1)

Проблема в том, что как только процесс поставит себе права обычного юзера, этот самый обычный юзер может поймать его отладчиком и сдампить память.

Убунтята для этого придумали sysctl kernel.yama.ptrace_scope, в принципе запрещающий юзерам цепляться к процессам, не являющихся чилдами отладчика. Мне всегда казалось это лютой дичью, но от такого вектора атаки защитит)

annulen ★★★★★
()
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.