Làm thế nào để tìm kiếm các tập tin với tập thuộc tính bất biến?


17

Vì lý do kiểm toán cấu hình, tôi muốn có thể tìm kiếm hệ thống tệp ext3 của mình để tìm các tệp có tập thuộc tính bất biến (thông qua chattr +i). Tôi không thể tìm thấy bất kỳ tùy chọn cho findhoặc tương tự làm điều này. Tại thời điểm này, tôi e rằng tôi sẽ phải viết kịch bản của riêng mình để phân tích lsattrđầu ra cho mỗi thư mục. Có một tiện ích tiêu chuẩn cung cấp một cách tốt hơn?


Tôi nên làm rõ rằng trong trường hợp của mình, tôi chỉ kiểm tra để quản lý cấu hình chứ không phải phát hiện xâm nhập, vì vậy tôi không phải lo lắng quá nhiều về các dòng mới, vì tôi biết tên tệp tôi đang làm việc sẽ không có họ Tuy nhiên, vấn đề về dòng mới đáng để ghi nhớ, vì vậy tôi sẽ để lại câu hỏi của mình.
tước

Câu trả lời:


9

Nó có thể được thực hiện một phần bằng cách chuyển greplệnh sang lsattrlệnh.

lsattr -R | grep +i

Tuy nhiên, tôi tin rằng khi bạn đề cập đến toàn bộ ext3hệ thống tập tin tìm kiếm có thể bao gồm /proc, /devvà một số thư mục khác mà nếu báo cáo một số lỗi bạn chỉ muốn bỏ qua. Bạn có thể chạy lệnh như,

lsattr -R 2>/dev/null | grep -- "-i-"

Bạn có thể muốn làm cho grepnghiêm ngặt hơn một chút bằng cách sử dụng grepcơ sở PCRE của mình để khớp rõ ràng hơn với "-i-".

lsattr -R 2>/dev/null | grep -P "(?<=-)i(?=-)"

Điều này sau đó sẽ làm việc cho các tình huống như thế này:

$ lsattr -R 2>/dev/null afile | grep -P "(?<=-)i(?=-)"
----i--------e-- afile

Nhưng là không hoàn hảo. Nếu có các thuộc tính bổ sung được bật xung quanh cờ bất biến, thì chúng ta sẽ không khớp với chúng và điều này sẽ bị đánh lừa bởi các tệp mà tên của chúng cũng khớp với mẫu trên, chẳng hạn như:

$ lsattr -R 2>/dev/null afile* | grep -P "(?<=-)i(?=-)"
----i--------e-- afile
-------------e-- afile-i-am

Chúng ta có thể thắt chặt mô hình hơn một chút như thế này:

$ lsattr -a -R 2>/dev/null afile* | grep -P "(?<=-)i(?=-).* "
----i--------e-- afile

Nhưng nó vẫn còn quá mong manh và sẽ yêu cầu điều chỉnh bổ sung tùy thuộc vào các tệp trong hệ thống tệp của bạn. Không đề cập đến như @StephaneChazele đã đề cập trong các bình luận rằng điều này có thể được chơi khá dễ dàng bằng cách bao gồm các dòng mới với tên tệp để bỏ qua mẫu trên grep.

Người giới thiệu

https://groups.google.com/forum/#!topic/alt.os.linux/LkatROg2SlM


1
Hà tôi đọc cùng một chủ đề và chuẩn bị đăng. Thay vào đó tôi sẽ thêm các tính năng bổ sung của tôi.
slm

@slm, rất mong bạn thực hiện bất kỳ thay đổi nào :)
Ramesh

2
Có lẽ không tốt cho việc kiểm toán vì người ta có thể giả mạo hoặc ẩn một tệp bất biến bằng cách có các ký tự dòng mới trong tên tệp với cách tiếp cận đó. Ngoài ra, không có gì lạ khi tên tập tin có -i-tên của nó (có 34 trên hệ thống tôi hiện đang đăng nhập). Có lẽ bạn cũng muốn -atùy chọn này
Stéphane Chazelas

1
Vì tò mò, những gì được +icho là trong ví dụ đầu tiên? Nó không làm việc cho tôi. Ngoài ra, grepping cho -i-giả định rằng các thuộc tính xuất hiện liền kề i(chẳng hạn a) là không được đặt.
tước

1
Tại sao không đơn giản là grep cho ^....i? Hoặc ít nhất một cái gì đó như ^[^ ]*inếu icó thể ở vị trí khác ngoài thứ năm.
Ruslan

6

Cho rằng mục đích của tập lệnh là kiểm toán, điều đặc biệt quan trọng là phải xử lý chính xác các tên tệp tùy ý, ví dụ như với các tên chứa dòng mới. Điều này làm cho không thể sử dụng lsattrđồng thời trên nhiều tệp, vì đầu ra của lsattrcó thể không rõ ràng trong trường hợp đó.

Bạn có thể lặp lại findvà gọi lsattrtrên một tệp cùng một lúc. Nó sẽ khá chậm mặc dù.

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 {} +

Tôi khuyên bạn nên sử dụng một ngôn ngữ ít cáu kỉnh như Perl, Python hoặc Ruby và tự mình thực hiện công việc lsattr. lsattrhoạt động bằng cách phát hành một FS_IOC_GETFLAGStòa nhà ioctl và lấy các cờ inode của tệp . Đây là một bằng chứng khái niệm Python.

#!/usr/bin/env python2
import array, fcntl, os, sys
FS_IOC_GETFLAGS = 0x80086601
EXT3_IMMUTABLE_FL = 0x00000010
count = 0
def check(filename):
    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)

1
FYI trên hệ thống của tôi FS_IOC_GETFLAGS0x80046601.
antonone

1
Giá trị của FS_IOC_GETFLAGSphụ thuộc vào sizeof(long). Xem ví dụ: lệnh bash sau đây để tìm hiểu những gì macro mở rộng trong C : gcc -E - <<< $'#include <linux/fs.h>\nFS_IOC_GETFLAGS' | tail -n1. Tôi nhận được từ nó biểu thức sau : (((2U) << (((0 +8)+8)+14)) | ((('f')) << (0 +8)) | (((1)) << 0) | ((((sizeof(long)))) << ((0 +8)+8))), đơn giản hóa (2U << 30) | ('f' << 8) | 1 | (sizeof(long) << 16).
Ruslan

3

Để xử lý các tên tệp tùy ý (bao gồm cả các tên chứa ký tự dòng mới), mẹo thông thường là tìm các tệp bên trong .//.thay vì .. Bởi vì //thông thường không thể xảy ra trong khi di chuyển ngang qua cây thư mục, bạn chắc chắn rằng //tín hiệu bắt đầu tên tệp mới trong đầu ra find(hoặc ở đây 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()}'

Lưu ý rằng đầu ra vẫn sẽ được phân tách dòng mới. Nếu bạn cần xử lý hậu kỳ, bạn sẽ phải điều chỉnh nó. Chẳng hạn, bạn có thể thêm một -v ORS='\0'để có thể cung cấp nó cho GNU xargs -r0.

Cũng lưu ý rằng lsattr -R(ít nhất 1.42.13) không thể báo cáo cờ của các tệp có đường dẫn lớn hơn PATH_MAX (thường là 4096), vì vậy ai đó có thể ẩn tệp bất biến đó bằng cách di chuyển thư mục mẹ của nó (hoặc bất kỳ thành phần đường dẫn nào dẫn đến nó, ngoại trừ chính nó là bất biến) vào một thư mục rất sâu.

Một công việc xung quanh sẽ được sử dụng findvới -execdir:

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

Bây giờ, với -print0, đó là hậu xử lý, nhưng nếu bạn có ý định làm bất cứ điều gì với các đường dẫn đó, hãy lưu ý rằng bất kỳ cuộc gọi hệ thống nào trên các đường dẫn tệp lớn hơn PATH_MAX vẫn sẽ bị lỗi và các thành phần thư mục có thể được đổi tên trong khoảng.

Nếu chúng ta nhận được một báo cáo đáng tin cậy về cây thư mục có khả năng ghi được bởi những người khác, thì có một vài vấn đề cố hữu đối với lsattrchính lệnh mà chúng ta cần đề cập:

  • cách lsattr -R .đi qua cây thư mục, nó phải tuân theo các điều kiện chủng tộc. Người ta có thể làm cho nó xuống các thư mục bên ngoài cây thư mục được định tuyến .bằng cách thay thế một số thư mục bằng symlink vào đúng thời điểm.
  • thậm chí lsattr -d filecó một điều kiện chủng tộc. Những thuộc tính này chỉ áp dụng cho các tập tin hoặc thư mục thông thường. Vì vậy, lsattrkhông một lstat()đầu tiên để kiểm tra xem các tập tin là trong những loại phải và sau đó không open()tiếp theo ioctl()để lấy các thuộc tính. Nhưng nó gọi open()mà không có O_NOFOLLOW(cũng không phải O_NOCTTY). Ai đó có thể thay thế filebằng một liên kết tượng trưng /dev/watchdogchẳng hạn giữa lstat()open()và khiến hệ thống khởi động lại. Nó nên làm open(O_PATH|O_NOFOLLOW)theo sau fstat(), openat()ioctl()ở đây để tránh các điều kiện cuộc đua.

2

Cảm ơn Ramesh, slm và Stéphane đã chỉ cho tôi đi đúng hướng (tôi đã bị mất công -Rtắc lsattr). Thật không may, cho đến nay không có câu trả lời nào hoạt động chính xác với tôi.

Tôi đã đưa ra những điều sau đây:

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

Điều này bảo vệ chống lại các dòng mới được sử dụng để làm cho một tập tin xuất hiện dưới dạng bất biến khi không. Nó không bảo vệ chống lại các tập tin được đặt là bất biến và có dòng mới trong tên tệp của chúng. Nhưng vì một tệp như vậy sẽ phải được tạo theo cách đó bằng root, tôi có thể tin tưởng rằng các tệp đó không tồn tại trên hệ thống tệp của tôi cho trường hợp sử dụng của tôi. (Phương pháp này không phù hợp để phát hiện xâm nhập trong trường hợp người dùng root có thể bị xâm phạm, nhưng sau đó không sử dụng cùng một lsattrtiện ích của hệ thống cũng thuộc sở hữu của cùng một người dùng root.)


Chỉ root mới có thể thêm bit bất biến vào một tệp, nhưng có khả năng những người dùng khác sau đó có thể đổi tên các thành phần đường dẫn dẫn đến các tệp đó, vì vậy đường dẫn tệp có thể chứa dòng mới. Ngoài ra, người dùng có thể tạo một đường dẫn tệp (không phải là bất biến) sẽ đánh lừa tập lệnh của bạn để nghĩ rằng một tệp khác là bất biến.
Stéphane Chazelas

2

Sử dụng find -execlà quá chậm, phân tích sản lượng lsattrkhông đáng tin cậy tương tự như củals , sử dụng Python như trong câu trả lời của Gilles yêu cầu để chọn hằng số cho ioctltùy thuộc vào việc trình thông dịch Python là 32 hoặc 64-bit ...

Vấn đề hiện tại là mức độ thấp hơn hoặc thấp hơn, vì vậy hãy chuyển sang cấp độ thấp hơn: C ++ không tệ như ngôn ngữ kịch bản :) Như một phần thưởng, nó có quyền truy cập vào các tiêu đề hệ thống C với toàn bộ sức mạnh của bộ tiền xử lý C.

Chương trình sau đây tìm kiếm các tệp không thay đổi, nằm trong một hệ thống tệp, tức là không bao giờ vượt qua các điểm gắn kết. Để tìm kiếm cây rõ ràng, vượt qua các điểm gắn kết nếu cần, xóa FTW_MOUNTcờ trong nftwcuộc gọi. Ngoài ra, nó không theo symlink. Để làm theo họ, loại bỏ FTW_PHYScờ.

#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;
    }
}

-1

Thay vì đường ống đầu ra đến grep, tại sao không sử dụng awk để chỉ khớp với 'i' trong trường đầu tiên của đầu ra?

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

Trên thực tế, tôi chạy nó hàng ngày qua cron để quét thư mục / etc trên hàng trăm máy chủ và gửi đầu ra tới syslog. Sau đó tôi có thể tạo báo cáo hàng ngày qua Splunk:

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

Đoạn mã đầu tiên của bạn có lỗi đánh máy và mã thứ hai của bạn không tìm thấy các tệp bất biến trên hệ thống của tôi.
depquid

Sửa lỗi chính tả trong lệnh đầu tiên. Có lẽ cái thứ hai không tìm thấy bất kỳ tập tin bất biến nào bởi vì không có tập tin nào?
Rootdev

Tôi không nhận thấy lệnh thứ hai chỉ nhìn vào /etc. Nhưng cả hai lệnh không chính xác tìm thấy một tệp không thay đổi được tạo bằngtouch `"echo -e "bogus\n---------i---e-- changeable"`"
depquid

Nó nói trong bài viết gốc của tôi rằng tôi đang chạy nó thông qua cron để quét thư mục / etc. Tôi không thể giúp nó nếu bạn không đọc bài viết hoặc lệnh trước khi chạy nó. Và vâng, bạn có thể có thể xây dựng một trường hợp cạnh để đánh lừa mọi tìm kiếm nếu bạn cảm thấy thích nó, nhưng vì bạn đã rất nhanh chóng chỉ ra lỗi đánh máy trong lệnh ban đầu của tôi (thiếu cuối cùng '), tôi sẽ chỉ ra rằng lệnh của bạn không hoạt động như được viết nên sẽ không tạo ra bất cứ điều gì! :-)
Rootdev

Lỗi của tôi. Hãy thử điều này:touch "`echo -e 'bogus\n---------i---e-- changeable'`"
depquid

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.