Как искать файлы с неизменным набором атрибута?

"Обычный" путь состоит в том, чтобы использовать DKMS (первоначально разработанный Dell для поддержки определенных драйверов для их серверов в соответствии с Linux). Даже Nvidia имеет теперь интеграцию их драйвера видеокарты с DKMS - что драйвер должен быть перекомпилирован с каждым обновлением ядра, также.

18
12.06.2014, 23:05
8 ответов

Спасибо Ramesh, slm и Stéphane за то, что указали мне правильное направление (я пропустил переключатель -R для lsattr). К сожалению, до сих пор ни один из ответов не сработал для меня правильно.

Я придумал следующее:

lsattr -aR .//. | sed -rn '/i.+\.\/\/\./s/\.\/\///p'

Это защищает от использования новых строк для того, чтобы файл выглядел неизменным, когда это не так. Это делает а не защиту от файлов, которые установлены как неизменяемые и имеют новые строки в именах файлов. Но так как такой файл должен быть сделан таким образом корневым, я могу быть уверен, что таких файлов не существует в моей файловой системе для моего случая использования. (Этот метод не подходит для обнаружения вторжений в случаях, когда пользователь root может быть скомпрометирован, но тогда и он не использует ту же самую утилиту системы lsattr, которая также принадлежит тому же пользователю root).

2
27.01.2020, 19:46
[1189238]Это можно частично выполнить, выполнив команду [1189660]grep[1189661] на команду [1189662]lsattr[1189663]. Однако, я полагаю, что при упоминании всей файловой системы [1189664]ext3[1189665] поиск может включать в себя [1189666]/proc[1189667] , [1189668]/dev[1189669] и некоторые другие каталоги, которые, если вы сообщите об ошибке, вы просто захотите проигнорировать. Скорее всего, вы можете запустить команду, так как, [12129], возможно, вы захотите сделать [1189670]grep[1189671] немного строже, используя средство PCRE [1189672]grep[1189673] для более явного совпадения с "-i-". [12130] Это сработает в таких ситуациях, как эта:[12131], но это несовершенство. Если вокруг неизменяемого флага включены дополнительные атрибуты, то мы не сможем их сопоставить, и это будет обмануто файлами, имена которых совпадают с вышеописанным шаблоном, например:[12132]Мы можем немного подтянуть шаблон так:[12133]Но он все равно слишком хрупкий и потребует дополнительной доработки в зависимости от файлов внутри вашей файловой системы. Не говоря уже о том, что [1189674]@StephaneChazeles[1189675] упоминал в комментариях, что это можно довольно легко воспроизвести, включив новые строки с именем файлов, чтобы обойти вышеописанный шаблон в [1189676]grep[1189677].[12134]Ссылки[12135]https://groups. google.com/forum/#!topic/alt.os.linux/LkatROg2SlM[12136]
9
27.01.2020, 19:46

Вместо того, чтобы строить вывод grep, почему бы просто не использовать awk, чтобы соответствовать только 'i' в первом поле вывода?

lsattr -Ra 2>/dev/null /|awk '$1 ~ /i/ && $1 !~ /^\// {print}'

На самом деле, я запускаю это ежедневно через cron, чтобы просканировать каталог /etc на сотнях серверов и отправить вывод в syslog. Затем я могу генерировать ежедневный отчет через Splunk:

lsattr -Ra 2>/dev/null /etc|awk '$1 ~ /i/ && $1 !~ /^\// {print "Immutable_file="$2}'|logger -p local0.notice -t find_immutable
-1
27.01.2020, 19:46

Учитывая, что целью сценария является аудит, особенно важно правильно работать с произвольными именами файлов, напримерс именами, содержащими символы новой строки. Это делает невозможным использование lsattr одновременно для нескольких файлов, поскольку в этом случае вывод lsattr может быть неоднозначным.

Вы можете выполнить рекурсию с помощью find и вызвать lsattr по одному файлу за раз. Хотя это будет довольно медленно.

find / -xdev -exec sh -c '
  for i do
     attrs=$(lsattr -d "$i"); attrs=${attrs%% *}
     case $attrs in
       *i*) printf "%s\0" "$i";;
     esac
  done' sh {} +

Я рекомендую использовать менее капризный язык, такой как Perl, Python или Ruby, и выполнять работу lsattr самостоятельно. lsattr запускает системный вызов FS_IOC_GETFLAGS ioctl и получает флаги inode файла . Вот доказательство концепции Python.

#!/usr/bin/env python2
import array, fcntl, os, sys
S_IFMT =  0o170000
S_IFDIR = 0o040000
S_IFREG = 0o100000
FS_IOC_GETFLAGS = 0x80086601
EXT3_IMMUTABLE_FL = 0x00000010
count = 0
def check(filename):
    mode = os.lstat(filename).st_mode
    if mode & S_IFMT not in [S_IFREG, S_IFDIR]:
        return
    fd = os.open(filename, os.O_RDONLY)
    a = array.array('L', [0])
    fcntl.ioctl(fd, FS_IOC_GETFLAGS, a, True)
    if a[0] & EXT3_IMMUTABLE_FL: 
        sys.stdout.write(filename + '\0')
        global count
        count += 1
    os.close(fd)
for x in sys.argv[1:]:
    for (dirpath, dirnames, filenames) in os.walk(x):
        for name in dirnames + filenames:
            check(os.path.join(dirpath, name))
if count != 0: exit(1)
6
27.01.2020, 19:46

Чтобы иметь дело с произвольными именами файлов (включая те, которые содержат символы новой строки), обычный трюк заключается в поиске файлов внутри . //. вместо . . Поскольку // обычно не может возникнуть при обходе дерева каталогов, вы уверены, что // сигнализирует о начале нового имени файла в find (или здесь lsattr -R ) вывод.

lsattr -R .//. | awk '
  function process() {
    i = index(record, " ")
    if (i && index(substr(record,1,i), "i"))
      print substr(record, i+4)
  }
  {
    if (/\/\//) {
      process()
      record=$0
    } else {
      record = record "\n" $0
    }
  }
  END{process()}'

Обратите внимание, что вывод по-прежнему будет разделен новой строкой. Если вам нужно его обработать, вам придется его адаптировать. Например, вы можете добавить -v ORS = '\ 0' , чтобы передать его в GNU xargs -r0 .

Также обратите внимание, что lsattr -R (по крайней мере 1.42.13) не может сообщать флаги файлов, путь которых больше, чем PATH_MAX (обычно 4096), поэтому кто-то может скрыть такой неизменяемый файл, переместив его родительский каталог (или любой из компонентов пути, которые к нему ведут, кроме самого себя, поскольку он неизменяемый) в очень глубокий каталог.

Чтобы обойти эту проблему, можно использовать find с -execdir :

find . -execdir sh -c '
  a=$(lsattr -d "$1") &&
    case ${a%% *} in
      (*i*) ;;
      (*) false
    esac' sh {} \; -print0

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

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

  • путь lsattr -R. просматривает дерево каталогов, это зависит от состояния гонки. Можно заставить его спускаться в каталоги за пределами дерева каталогов, маршрутизируемых по адресу . путем замены некоторых каталогов символическими ссылками в нужный момент.
  • даже lsattr -d файл имеет состояние гонки. Эти атрибуты применимы только к обычным файлам или каталогам.Итак, lsattr сначала выполняет lstat () , чтобы проверить правильность типа файла, а затем выполняет open () , а затем ioctl () для получения атрибутов. Но он вызывает open () без O_NOFOLLOW (или O_NOCTTY). Кто-то может заменить файл символической ссылкой на / dev / watchdog , например, между lstat () и open () и вызвать систему перезагрузить. Он должен выполнить open (O_PATH | O_NOFOLLOW) , за которым следует fstat () , openat () и ioctl () , чтобы избежать условия гонки.
3
27.01.2020, 19:46

Использование find -exec слишком медленное, вывод синтаксического анализа lsattr ненадежен, как и ls , использование Python, как в ответе Жиля, требует выбора константы для ioctl в зависимости от того, является ли интерпретатор Python 32- или 64-битным...

Проблема здесь более-менее низкоуровневая, так что давайте перейдем к более низкому уровню: C++ не так уж и плох как скриптовый язык :) В качестве бонуса он имеет доступ к системным заголовкам C с полной мощью препроцессора C.

Следующая программа ищет неизменяемые файлы, оставаясь в пределах одной файловой системы, т.е. никогда не пересекает точки монтирования. Для поиска видимого дерева, пересекая точки монтирования по мере необходимости, удалите флаг FTW_MOUNT в вызове nftw. Также он не следует символическим ссылкам. Чтобы следовать им, снимите флаг FTW_PHYS.

#define _FILE_OFFSET_BITS 64
#include <iostream>
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
#include <sys/stat.h>
#include <ftw.h>

bool isImmutable(const char* path)
{
    static const int EXT3_IMMUTABLE_FLAG=0x10;

    const int fd=open(path,O_RDONLY|O_NONBLOCK|O_LARGEFILE);
    if(fd<=0)
    {
        perror(("Failed to open file \""+std::string(path)+"\"").c_str());
        return false;
    }
    unsigned long attrs;
    if(ioctl(fd,FS_IOC_GETFLAGS,&attrs)==-1)
    {
        perror(("Failed to get flags for file \""+std::string(path)+"\"").c_str());
        close(fd);
        return false;
    }
    close(fd);
    return attrs & EXT3_IMMUTABLE_FLAG;
}

int processPath(const char* path, const struct stat* info, int type, FTW* ftwbuf)
{
    switch(type)
    {
    case FTW_DNR:
        std::cerr << "Failed to read directory: " << path << "\n";
        return 0;
    case FTW_F:
        if(isImmutable(path))
            std::cout << path << '\n';
        return 0;
    }
    return 0;
}

int main(int argc, char** argv)
{
    if(argc!=2)
    {
        std::cerr << "Usage: " << argv[0] << " dir\n";
        return 1;
    }
    static const int maxOpenFDs=15;
    if(nftw(argv[1],processPath,maxOpenFDs,FTW_PHYS|FTW_MOUNT))
    {
        perror("nftw failed");
        return 1;
    }
}
2
27.01.2020, 19:46

Используйте это для поиска всех неизменяемых файлов:

lsattr -laR | grep "Immutable" | awk {'print $1'}
0
05.05.2020, 00:45

Вероятно, немного поздно добавлять, но я создал три разных файла с неизменяемыми битами в разных подпапках -моего каталога /etc.

Вот как я их нашел:

# lsattr -aR /etc 2>/dev/null | awk '{ print $1 " " $2 }' | grep -w i
0
25.12.2020, 15:19

Теги

Похожие вопросы