Bagaimana cara mencari file dengan set atribut yang tidak dapat diubah?

17

Untuk alasan audit konfigurasi, saya ingin dapat mencari filesystem ext3 saya untuk file yang memiliki set atribut yang tidak dapat diubah (via chattr +i). Saya tidak dapat menemukan opsi untuk findatau serupa yang melakukan ini. Pada titik ini, saya khawatir saya harus menulis skrip saya sendiri untuk mengurai lsattroutput untuk setiap direktori. Apakah ada utilitas standar yang menyediakan cara yang lebih baik?

depquid
sumber
Saya seharusnya sudah mengklarifikasi bahwa dalam kasus saya, saya mengaudit hanya untuk manajemen konfigurasi, bukan deteksi intrusi, jadi saya tidak perlu terlalu khawatir tentang baris baru, karena saya tahu nama file yang saya kerjakan tidak akan memiliki mereka. Meskipun demikian, masalah baris baru patut diingat, jadi saya akan membiarkan pertanyaan saya apa adanya.
depquid

Jawaban:

9

Ini dapat sebagian diselesaikan dengan memipipkan grepperintah ke lsattrperintah.

lsattr -R | grep +i

Namun, saya percaya ketika Anda menyebutkan seluruh ext3sistem file pencarian mungkin melibatkan /proc, /devdan beberapa direktori lain yang jika melaporkan beberapa kesalahan Anda hanya ingin mengabaikan. Anda mungkin dapat menjalankan perintah sebagai,

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

Anda mungkin ingin membuatnya grepsedikit lebih ketat dengan menggunakan grepfasilitas PCRE agar lebih cocok dengan "-i-".

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

Ini kemudian akan berfungsi untuk situasi seperti ini:

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

Tetapi tidak sempurna. Jika ada atribut tambahan yang diaktifkan di sekitar flag yang tidak dapat diubah, maka kami tidak akan mencocokkannya, dan ini akan dibodohi oleh file yang namanya cocok dengan pola di atas, seperti ini:

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

Kita bisa sedikit memperketat polanya seperti ini:

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

Tapi itu masih agak terlalu rapuh dan akan membutuhkan penyesuaian tambahan tergantung pada file dalam sistem file Anda. Belum lagi sebagai @StephaneChazeles telah disebutkan dalam komentar bahwa ini dapat diatasi dengan cukup mudah dengan memasukkan baris baru dengan nama file untuk memotong pola di atas grep.

Referensi

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

Ramesh
sumber
1
Ha saya membaca utas yang sama dan akan memposting. Saya akan menambahkan ekstra saya ke milik Anda sebagai gantinya.
slm
@slm, Anda dipersilakan melakukan perubahan apa pun :)
Ramesh
2
Mungkin tidak baik untuk audit karena seseorang dapat memalsukan atau menyembunyikan file yang tidak dapat diubah dengan memiliki karakter baris baru dalam nama file dengan pendekatan itu. Juga, tidak jarang nama file ada -i-dalam namanya (ada 34 pada sistem saya saat ini login). Anda mungkin juga menginginkan -aopsi ini
Stéphane Chazelas
1
Hanya karena penasaran, untuk apa +icontoh pertama? Itu tidak bekerja untuk saya. Juga, menerima -i-asumsi mengasumsikan bahwa atribut yang muncul berdekatan i(seperti a) tidak disetel.
depquid
1
Mengapa tidak menerima saja ^....i? Atau setidaknya sesuatu seperti ^[^ ]*ijika ibisa berada di posisi lain dari kelima.
Ruslan
6

Mengingat bahwa tujuan skrip adalah mengaudit, sangat penting untuk berurusan dengan nama file yang sewenang-wenang, misalnya dengan nama yang mengandung baris baru. Ini membuatnya tidak mungkin untuk digunakan lsattrpada banyak file secara bersamaan, karena output dari lsattrdapat menjadi ambigu dalam kasus itu.

Anda dapat berulang dengan finddan memanggil lsattrsatu file pada suatu waktu. Ini akan sangat lambat.

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

Saya sarankan menggunakan bahasa yang kurang rewel seperti Perl, Python atau Ruby dan melakukan pekerjaan lsattrsendiri. lsattrberoperasi dengan mengeluarkan FS_IOC_GETFLAGSsyscall ioctl dan mengambil flag inode file . Inilah bukti konsep 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)
Gilles 'SANGAT berhenti menjadi jahat'
sumber
1
FYI di sistem saya FS_IOC_GETFLAGSadalah 0x80046601.
antonone
1
Nilai FS_IOC_GETFLAGStergantung pada sizeof(long). Lihat misalnya perintah bash berikut untuk mencari tahu apa yang mengembang makro dalam C: gcc -E - <<< $'#include <linux/fs.h>\nFS_IOC_GETFLAGS' | tail -n1. Saya mendapatkan dari itu ungkapan berikut:, (((2U) << (((0 +8)+8)+14)) | ((('f')) << (0 +8)) | (((1)) << 0) | ((((sizeof(long)))) << ((0 +8)+8)))yang disederhanakan menjadi (2U << 30) | ('f' << 8) | 1 | (sizeof(long) << 16).
Ruslan
3

Untuk menghadapi nama file sewenang-wenang (termasuk yang mengandung karakter newline), yang trik yang biasa adalah untuk menemukan file di dalam .//.bukan .. Karena //biasanya tidak dapat terjadi saat melintasi pohon direktori, Anda yakin bahwa //sinyal awal nama file baru di find(atau di sini lsattr -R) output.

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()}'

Perhatikan bahwa output masih akan dipisahkan baris baru. Jika Anda perlu mengolahnya, Anda harus menyesuaikannya. Misalnya, Anda bisa menambahkan -v ORS='\0'agar dapat memberinya makan ke GNU xargs -r0.

Juga perhatikan bahwa lsattr -R(setidaknya 1,42.13) tidak dapat melaporkan flag file yang jalurnya lebih besar dari PATH_MAX (biasanya 4096), sehingga seseorang dapat menyembunyikan file yang tidak dapat diubah tersebut dengan memindahkan direktori induknya (atau komponen jalur mana pun yang mengarah ke itu, kecuali dirinya sendiri karena tidak dapat diubah) ke dalam direktori yang sangat dalam.

Pekerjaan di sekitar akan digunakan finddengan -execdir:

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

Sekarang, dengan -print0, itu pasca-proses, tetapi jika Anda berniat untuk melakukan apa pun dengan jalur tersebut, perhatikan bahwa setiap panggilan sistem pada jalur file yang lebih besar dari PATH_MAX akan tetap gagal dan komponen direktori bisa diganti namanya dalam interval tersebut.

Jika kita ingin mendapatkan laporan yang dapat diandalkan tentang pohon direktori yang berpotensi dapat ditulis oleh orang lain, ada beberapa masalah yang melekat pada lsattrperintah itu sendiri yang perlu kita sebutkan:

  • cara lsattr -R .melintasi pohon direktori, itu tergantung pada kondisi balapan. Orang dapat membuatnya turun ke direktori di luar pohon direktori yang dirutekan .dengan mengganti beberapa direktori dengan symlink pada saat yang tepat.
  • bahkan lsattr -d filememiliki kondisi balapan. Atribut tersebut hanya berlaku untuk file atau direktori biasa. Begitu lsattrjuga yang lstat()pertama untuk memeriksa bahwa file tersebut dari jenis yang tepat dan kemudian open()diikuti oleh ioctl()untuk mengambil atribut. Tapi itu panggilan open()tanpa O_NOFOLLOW(atau O_NOCTTY). Seseorang dapat mengganti filedengan symlink /dev/watchdogmisalnya antara lstat()dan open()dan menyebabkan sistem reboot. Itu harus dilakukan open(O_PATH|O_NOFOLLOW)diikuti oleh fstat(), openat()dan di ioctl()sini untuk menghindari kondisi balapan.
Stéphane Chazelas
sumber
2

Terima kasih kepada Ramesh, slm dan Stéphane karena telah menunjuk saya ke arah yang benar (saya kehilangan -Rsaklar untuk lsattr). Sayangnya, sejauh ini tidak ada jawaban yang berfungsi dengan baik untuk saya.

Saya datang dengan yang berikut:

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

Ini melindungi terhadap baris baru yang digunakan untuk membuat file tampak tidak dapat diubah ketika tidak. Itu tidak melindungi terhadap file yang ditetapkan sebagai tidak dapat diubah dan memiliki baris baru dalam nama file mereka. Tetapi karena file seperti itu harus dibuat seperti itu oleh root, saya dapat yakin bahwa file seperti itu tidak ada pada sistem file saya untuk kasus penggunaan saya. (Metode ini tidak cocok untuk deteksi intrusi dalam kasus di mana pengguna root dapat dikompromikan, tetapi kemudian keduanya tidak menggunakan lsattrutilitas sistem yang sama yang juga dimiliki oleh pengguna root yang sama.)

depquid
sumber
Hanya root yang dapat menambahkan bit yang tidak dapat diubah ke file, tetapi berpotensi pengguna lain nantinya dapat mengganti nama komponen path yang mengarah ke file-file tersebut, sehingga path file mungkin berisi baris baru. Juga pengguna dapat membuat jalur file (tidak dapat diubah) yang akan membodohi skrip Anda dengan berpikir file lain tidak dapat diubah.
Stéphane Chazelas
2

Menggunakan find -execterlalu lambat, parsing output lsattrtidak dapat diandalkan mirip denganls , menggunakan Python seperti dalam jawaban oleh Gilles harus memilih konstanta untuk ioctlbergantung pada apakah interpreter Python 32- atau 64-bit ...

Masalah yang dihadapi adalah level yang lebih rendah, jadi mari kita tingkat yang lebih rendah: C ++ tidak seburuk bahasa scripting :) Sebagai bonus, ia memiliki akses ke header sistem C dengan kekuatan penuh dari preprosesor C.

Program berikut mencari file yang tidak dapat diubah, tetap berada dalam satu sistem file, yaitu tidak pernah melewati titik mount. Untuk mencari pohon yang terlihat, melintasi titik pemasangan seperlunya, hapus FTW_MOUNTtanda pada nftwpanggilan. Juga tidak mengikuti symlink. Untuk mengikuti mereka, hapus FTW_PHYSbendera.

#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;
    }
}
Ruslan
sumber
-1

Alih-alih menyalurkan output ke grep, mengapa tidak menggunakan awk untuk mencocokkan 'i' di bidang pertama dari output?

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

Bahkan, saya menjalankan ini setiap hari melalui cron untuk memindai direktori / etc pada ratusan server dan mengirimkan hasilnya ke syslog. Saya kemudian dapat menghasilkan laporan harian melalui Splunk:

lsattr -Ra 2>/dev/null /etc|awk '$1 ~ /i/ && $1 !~ /^\// {print "Immutable_file="$2}'|logger -p local0.notice -t find_immutable
Rootdev
sumber
Cuplikan kode pertama Anda memiliki kesalahan ketik dan yang kedua tidak menemukan file yang tidak dapat diubah pada sistem saya.
depquid
Memperbaiki kesalahan ketik pada perintah pertama. Mungkin yang kedua tidak menemukan file yang tidak dapat diubah karena tidak ada?
Rootdev
Saya tidak melihat perintah kedua hanya melihat /etc. Namun kedua perintah tersebut secara keliru menemukan file yang tidak dapat diubah yang dibuat dengantouch `"echo -e "bogus\n---------i---e-- changeable"`"
depquid
Ia mengatakan dalam posting asli saya bahwa saya menjalankannya melalui cron untuk memindai direktori / etc. Saya tidak dapat menahannya jika Anda tidak membaca pos atau perintah sebelum menjalankannya. Dan ya, Anda mungkin dapat membuat case edge untuk mengelabui setiap pencarian jika Anda menyukainya, tetapi karena Anda begitu cepat menunjukkan kesalahan ketik pada perintah asli saya (hilang yang terakhir '), saya akan menunjukkan bahwa perintah Anda tidak berfungsi seperti yang tertulis sehingga tidak akan membuat apa pun! :-)
Rootdev
Salahku. Coba ini:touch "`echo -e 'bogus\n---------i---e-- changeable'`"
depquid