avva: (Default)
[personal profile] avva
Эта запись будет интересна, боюсь, только тем, кто разбирается в Юниксе и особенно в Линуксе.

На израильской линуксовской рассылке кто-то спросил помощь зала в решении проблемы. Проблема следующая. У меня есть процесс, который бежит с администраторскими привилегиями (как root, короче). Мне нужно сделать так, чтобы с помощью обычных ps/top этот факт нельзя было обнаружить: чтобы они показывали его бегущим под другим юзером. При этом можно предположить, что у меня есть root-доступ к этой системе, но я ограничен в некоторых своих действиях. Я не могу менять ядро, загружать новые модули в ядро, а также менять стандартные запускаемые файлы ps/top и стандартные библиотеки. Вопрос: что ещё я могу сделать?

Проблема сформулирована достаточно расплывчато, т.к. неясно в частности, кого нужно обмануть: человека, который запускает эти ps/top, или какой-нибудь скрипт. Но оставим эту расплывчатость, как она есть, и попытаемся придумать какие-нибудь методы. Способ “сменить PATH данному юзеру, чтобы запускались не стандартные ps/top, а другие” тоже отметём, как слишком тривиальный/очевидный/легкообнаруживаемый. Что остаётся?

Я придумал три штуки:

1) это достаточно серьёзное извращение ;), но можно сменить имя рута на данной системе, т.е. назвать uid 0 другим именем вместо root. Неискушённого юзера это вполне может обмануть. Недостаток: возможно, ломаем кучу скриптов/программ, которые полагаются на то, что uid 0 = root. В принципе, строго говоря, они все дефективны, если на это полагаются, но на практике, думаю, они на каждом шагу, в том числе среди скриптов, управляющих процессом загрузки системы.

Добавить ещё одну запись в /etc/passwd с uid 0 и другим именем не помогает, т.к. ps/top пользуются getpwuid(), а она берёт первую подходящую строку. Заставить все программы видеть одного юзера с uid 0, а ps/top другого, таким способом не выйдет.

2) ставим LD_PRELOAD: либо для данного юзера в его стартовых скриптах (рассчитывая на то, что он в этом не разбирается), либо общесистемно в /etc. В LD_PRELOAD ставим имя небольшой библиотеки, которая перекидывает на себя вызовы функции open(). ps/top читают информацию о процессах из "/proc/[pid]/stat" ; значит, наша фальшивая open() будет проверять, не пытаются ли открыть этот файл. Если пытаются, она копирует его в другое место (в /tmp например), меняя при этом нужное поле в нём, потом открывает его вместо настоящего и возвращает этот дескриптор. Тогда ps/top прочитают как миленькие из этого дескриптора и закроют его.

3) программа, бегущая как root, во время своей работы вызывает mount() и ставит поверх /proc/[my pid]/ копию tmpfs. При этом она держит открытый дескриптор старой директории. Раз в секунду она копирует все файлы и симлинки из старой директории в tmpfs, изменяя при этом файл stat, и маскируя свой настоящий uid. Всё.

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

В общем, это всё, что было предложено (идею номер 2) независимо предложили другие участники тоже). Да, есть ещё такая штука, как capabilities, когда программа может бежать не как root, но иметь некоторые рутовские привилегии. Но вроде бы в текущих версиях Линукса она работает плохо, куча багов, и поломаны user-mode утилиты для неё.

Есть ещё что-то существенное, интересно?

Date: 2004-09-01 01:30 pm (UTC)
From: [identity profile] dimrub.livejournal.com
Ну, чисто для прикола, если предполагается, что тот, кто запускает ps/top, подключается через telnet - заменить telnetd. Либо хакнуть его любимый шелл (или все шеллы, какие есть в системе). Плюс tty заодно хакнуть.

А ps/top какой uid показывают? Effective?

Date: 2004-09-01 01:31 pm (UTC)
From: [identity profile] avva.livejournal.com
Effective, да, с этой стороны не подкопаться (забыл написать).

Date: 2004-09-01 01:44 pm (UTC)
From: [identity profile] meshko.livejournal.com
Ну так а нельзя бегать видом какого-то юзера и менять effective на 0 только когда надо? С одной стороны это, конечно, можно заметить (если вовремя запустить ps), но с другой стороны два другие решения (первое я не засчитываю как непрактичное) тоже легко можно заметить, особенно с mount. Хотя, конечно, про mount это красиво.

Date: 2004-09-01 02:00 pm (UTC)
From: [identity profile] avva.livejournal.com
Гм, да, действительно. Я сначала подумал, что так нельзя, потому что setuid() в Линуксе не позволяет руту сохранить 0 в saved uid, а потом вернуться, это сделано специально. Но если пользоваться setreuid(), то можно поменять местами real/effective uid, не используя saved uid, потерять таким образом рут, а потом поменять их обратно и вернуть.

Хороший вариант, но не действует в случае, если привилегии нужно использовать постоянно и случайный запуск ps с высокой вероятностью на них наткнётся.

Date: 2004-09-01 02:07 pm (UTC)
From: [identity profile] meshko.livejournal.com
Чего-то про saved uid я торможу или это новая штука в 2.6, который я ещё не видел, но в 2.4 я только что написал вот так:

main()
{
seteuid(500);
while(1)
{
seteuid(0);
unlink("/tmp/test");
open("/tmp/test", O_CREAT, 0644);
seteuid(500);
sleep(1);
}
}


и это работает. Вопрос, конечно, в том, что более заметно. К тому же такая команда
ps -eo uid,ruid,pid,cmd
меня сразу выводит на чистую воду.

И вообще, на такие вопросы в рассылках надо отвечать, что с домашними заданиями и написаниями троянов мы тут не помогаем ;)

Date: 2004-09-01 03:20 pm (UTC)
From: [identity profile] avva.livejournal.com
saved uid не новая штука, просто малоизвестная относительно. Но я запутался с ней действительно, Вы правы, можно так.

Вопрос в том, какие есть syscalls, и как библиотечные функции user mode ими пользуются. syscalls есть такие: setreuid, setuid, setresuid. Переменные у каждого процесса есть такие: euid, ruid (называется просто uid на самом деле), suid (saved uid).

Происхождение функций:

setuid() - SysV/POSIX
setreuid() - BSD
setresuid() - Linux

suid придумали в SysV/POSIX вот для чего. Предположим, я юзер 1 и запускаю setuid-файл юзера 2. Теперь я бегу с ruid=1, euid=2. Я хочу сделать что-то insecure. Для этого я возвращаюсь вначале к своему euid: делаю setuid(1). Теперь я бегу с ruid=1, euid=1. Но теперь я не могу вернуть себе обратно привилегии юзера 2. Если в SysV стоит расширение SAVED_IDS, или если это POSIX, где это стало частью стандарта, то у меня есть suid, который при запуске был поставлен тоже в 2. Когда я делаю setuid(), мне разрешается вернуть себе значение suid, а не только ruid. Это позволяет мне обойти эту проблему. Заметьте, что в SysV ruid я вообще не меняю в принципе. Кроме того, в случае, если "юзер 2" это на самом деле рут, то для меня эта дорога закрыта: когда я делаю setuid(1) и меняю euid с 0 на 1, то suid тоже получает этот 1, специально, чтобы я не мог вернуться к руту. Так решили разработчики POSIXа.

В BSD всё было проще. У них не было (поначалу) никаких suid. Есть seteuid(), который позволяет мне ставить euid во что угодно, если я рут, и в ruid, если я не рут. Есть расширенный вызов setreuid(), который позволяет мне ставит сразу euid и ruid, причём значение каждого должно (для не-рута) быть либо текущим ruid, либо текущим euid. Это позволяет мне поменять местами euid и ruid, а потом поменять обратно (как в Вашей программе); таким образом сбросить рут, а потом вернуть. Ещё в BSD был свой setuid(), но он ставил сразу ruid и euid в одно значение (а потом и suid, когда в BSD его добавили).

Теперь как Линукс с этим справляется. У него есть самый расширенный свой нестандартный setresuid(), который позволяет изменить все три, причём рут может всё, а другие могут каждый из них поставить в любое из трёх старых значений, так что можно делать всякие пермутации. У него есть setreuid(), который, как BSD'шный setreuid(), ставит сразу новые ruid и euid, но! полезная штука: если при этом меняется euid с 0 на что-то другое, он сохраняет euid=0 в suid, и потом с помощью того же setreuid() можно вернуть себе привилегии. Почему это удобно: потому что семантика setreuid() такая же, как у BSD, но при этом можно поставить себе ненулевые как euid, так и ruid, и всё равно смочь потом вернуться к руту с помощью suid. Т.е. это лучше, чем Ваш пример с seteuid(): setreuid() позволяет замаскироваться так, что и ps -eo uid,ruid не поможет. Правда, поможет ps -eo uid,ruid,suid , но надо об этом знать ;)

Наконец, у него есть seteuid(), которого нет на уровне syscalls, он сделан через setresuid() внутри glibc.

Старая программа, написанная для SysV, использует setuid() и будет работать без изменений. Старая программа, написанная для BSD, использует setuid() или setreuid() и будет работать без изменений. Программа, написанная для Линукса, может использовать setreuid(), помня при этом об удобной возможности полностью скинуть рут из euid и ruid, а потом вернуть, или может использовать нестандартный setresuid(), позволяющий больше всех.

Вот, кажется, ничего не упустил.

страшновато

Date: 2004-09-01 03:28 pm (UTC)
From: [identity profile] meshko.livejournal.com
Спасибо, я всегда ленился польностью с этим разобраться...

ps -eo uid,ruid не поможет. Правда, поможет ps -eo uid,ruid,suid , но надо об этом знать ;)

:))

Date: 2004-09-01 01:47 pm (UTC)
From: [identity profile] ex-gregbg715.livejournal.com
можно совместить второй вариант с первым.

сделать два /etc/passwd и в open() смотреть, как называется открывающий процесс и, в зависимости от этого, открывать тот или другой /etc/passwd.

Date: 2004-09-01 01:49 pm (UTC)
From: [identity profile] avva.livejournal.com
Тогда уже легче просто второй, без первого, это только усложняет.

Date: 2004-09-01 01:48 pm (UTC)
From: [identity profile] uniqs.livejournal.com
Жаль, что нельзя kernel перестроить. Уж больно сюда PF_HIDDEN подходит.

Date: 2004-09-01 01:56 pm (UTC)
From: [identity profile] avva.livejournal.com
С перестройкой ядра, ясно, веселее ;)

Date: 2004-09-01 02:08 pm (UTC)
From: [identity profile] bugabuga.livejournal.com
А обозвать себя, скажем "top" (/my/evil/programs/top), создать фальшивый процесс с пользовательским уином и "правильным" именем не подойдёт? :) Или народ подозрительный и будет кричать "а что это тут top под рутом постянно висит"? :)

Date: 2004-09-01 03:23 pm (UTC)
From: [identity profile] avva.livejournal.com
Не, слишком очевидно ;)

Date: 2004-09-01 02:10 pm (UTC)
From: [identity profile] vsha.livejournal.com
Это больше о применение, чем ответ на поставленный вопрос.
Я помню как применял что-то подобное, когда строил honeypot для одной из гос. компании. Не помню конкретно как сделал,но код для этого не менял, но задача была похожая - когда хаккер вламывается в систему, (хаккер не новайс), он не должен обнаружить процесса. Код менял для скрытного лога, который шёл с последовательного порта на другой компьютер.

Date: 2004-09-01 02:39 pm (UTC)
From: [identity profile] kot-begemot.livejournal.com
Небольшая вариация на тему третьего подхода (хотя я её придумал независимо, благо фильтр на файловую систему - мой любимый трюк в ядре и ответ почти на все вопросы).
Пишем простенький фильтра для /proc файловой системы и подгружаем как модуль (или статически запихиваем в ядро). В фильтре перехватываем open и lookup на все /proc/pid/fd. Монтируем этот фильтр при загрузке. Теперь при вызове lookup/open смотрим на current и выясняем, является ли он ps/top. Если да - подменяем виртуальную таблицу inode своей с изменённой реализацией getattr.
Всё.

Date: 2004-09-01 02:44 pm (UTC)
stas: (Default)
From: [personal profile] stas
Если загружаемый модуль будет висеть не только на open, но и на read (ну ещё seek для верности), то копировать особо и не надо будет. Остаётся, конечно, ещё mmap, но вроде как его утилиты из procps не используют. Зато совсем не видно будет - никаких лишних писаний в /tmp.

Date: 2004-09-01 03:22 pm (UTC)
From: [identity profile] avva.livejournal.com
Да, но это более муторно и медленно (особенно если делать LD_PRELOAD для всех в /etc, то мы теперь перехватываем read() вообще всех программ, некультурно как-то ;) open() хотя бы намного реже вызывается). Можно, как подсказал кто-то, скопировать в /tmp или куда угодно ещё, открыть и сразу сделать unlink() перед возвращением дескриптора в ps/top; тогда никто ничего и не увидит, имени файла нигде нет, сам файл тихо умрёт после close() оригинального процесса.

Date: 2004-09-02 07:12 am (UTC)
stas: (Ну-ка)
From: [personal profile] stas
lsof видит открытые файлы, даже после unlink :)

Date: 2004-09-02 07:12 am (UTC)
stas: (Default)
From: [personal profile] stas
Да и в strace могут лишние open'ы быть заметны...

Date: 2004-09-01 05:22 pm (UTC)
From: [identity profile] bish0nen.livejournal.com
3) работать просто не будет. Все файлы под /proc/$$/ они, в общем-то, не простые файлы, а золотые - это куски кода ядра экспортируются в file system namespace файлами, например:

jism pts/2 ~/src 10053:<0>* cp /proc/$$/mem /tmp/mem
cp: reading `/proc/21677/mem': No such process


Ну итд. Часть из них, впрочем, скопируется, но подставу заметить легко: в procfs у них длина - нуль, а у копии (/proc/$$/stat, например) будет ненулевая. Единственный способ сделать что-то подобное 3) - это написать ядерный модуль со своей overlay file system, (однако не во всех версиях Линукса файловые системы stackable) и смонтировать её поверх procfs, который будет смотреть, на какой файл из procfs пришёл запрос, и либо подделать ответ либо отправить запрос по стеку дальше на обработку. Очень громоздко и легко обнаруживается.

Самый эффективный способ - если программа имеет права суперпользователя - это залезть в /dev/kmem и поковырять таблицу процессов. Если securelevel ядра не равен нулю, то и это бесполезно.

Date: 2004-09-01 05:40 pm (UTC)
From: [identity profile] avva.livejournal.com
Да нет, почему же не сработает? Ясно, что копия будет не идеальная; но задача стоит - обмануть ps и top, а они ничего, кроме stat, не читают вообще. Я предложил копировать всё остальное (mem и подобные ему файлы не стоит и пытаться, ясно) просто чтобы приятнее выглядело. Конечно, если юзер достаточно умён, чтобы знать о /proc, знать об этой директории, сделать в ней ls -l и понять, что ненулевой размер stat подозрителен - тогда да. Но таких мало.

А ковыряние таблицы процессов - это зачем? Того же можно добиться вполне стандартными вызовамы setreuid() и сотоварищи. Какая разница, кто изменит current->euid, функция sys_setreuid() или прямой доступ к /dev/kmem?

Date: 2004-09-02 02:42 am (UTC)
From: [identity profile] averros.livejournal.com
Перехватывать системные вызовы от ps :) ptrace is your friend.

Date: 2004-09-02 02:52 am (UTC)
From: [identity profile] avva.livejournal.com
А ps не делает никаких интересных с этом смысле вызовов ;) Он читает /proc/[pid]/stat, а потом ещё вызывает getpwuid(), чтобы узнать username, но это всё остаётся в user space.

Date: 2004-09-02 03:09 am (UTC)
From: [identity profile] averros.livejournal.com
Ну так можно перехватывать те read-ы и open-ы, и подсовывать свои данные :) Против лома нет приёма, против интерфейса отладчика - тоже :)

Date: 2004-09-02 03:15 am (UTC)
From: [identity profile] avva.livejournal.com
;) да, так можно.

December 2025

S M T W T F S
  123 4 56
78 9 10 11 1213
1415 1617181920
21 22 23 24 2526 27
2829 30 31   

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jan. 2nd, 2026 09:47 am
Powered by Dreamwidth Studios