วิธีค้นหาไฟล์ที่มีการตั้งค่าแอตทริบิวต์ที่ไม่เปลี่ยนรูปแบบ?


17

สำหรับเหตุผลการตรวจสอบการกำหนดค่าฉันต้องการค้นหาไฟล์ระบบ ext3 ของฉันเพื่อหาไฟล์ที่มีชุดคุณลักษณะที่ไม่เปลี่ยนรูปแบบ (ผ่านchattr +i) ฉันไม่พบตัวเลือกใด ๆfindหรือคล้ายกันที่ทำสิ่งนี้ ณ จุดนี้ฉันเกรงว่าฉันจะต้องเขียนสคริปต์ของตัวเองเพื่อแยกวิเคราะห์lsattrผลลัพธ์สำหรับแต่ละไดเรกทอรี มียูทิลิตี้มาตรฐานที่ให้วิธีที่ดีกว่าหรือไม่?


ฉันควรชี้แจงว่าในกรณีของฉันฉันกำลังตรวจสอบเพื่อจัดการการตั้งค่าเท่านั้นไม่ใช่การตรวจจับการบุกรุกดังนั้นฉันจึงไม่ต้องกังวลเรื่องบรรทัดใหม่มากเกินไปเพราะฉันรู้ว่าชื่อไฟล์ที่ฉันใช้งานจะไม่มี พวกเขา อย่างไรก็ตามปัญหาของการขึ้นบรรทัดใหม่นั้นควรระลึกไว้เสมอดังนั้นฉันจะทิ้งคำถามไว้ตามที่เป็นอยู่
ตัดสิทธิ์

คำตอบ:


9

สามารถทำได้บางส่วนโดยการไพพ์grepคำสั่งไปยังlsattrคำสั่ง

lsattr -R | grep +i

แต่ผมเชื่อว่าเมื่อคุณพูดถึงทั้งext3ระบบแฟ้มค้นหาอาจเกี่ยวข้องกับการ/proc, /devและบางไดเรกทอรีอื่น ๆ ซึ่งถ้ารายงานข้อผิดพลาดบางอย่างที่คุณเพียงแค่ต้องการที่จะไม่สนใจ คุณอาจเรียกใช้คำสั่งเป็น

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

คุณอาจต้องการทำให้grepเข้มงวดมากขึ้นอีกเล็กน้อยโดยใช้เครื่องมือgrepอำนวยความสะดวก PCRE เพื่อจับคู่ "-i-" อย่างชัดเจนยิ่งขึ้น

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

สิ่งนี้จะทำงานในสถานการณ์เช่นนี้:

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

แต่ไม่สมบูรณ์ หากมีการเปิดใช้งานแอตทริบิวต์เพิ่มเติมรอบ ๆ แฟล็กที่ไม่เปลี่ยนรูปเราจะไม่จับคู่และจะถูกหลอกโดยไฟล์ที่ชื่อของชื่อนั้นตรงกับรูปแบบด้านบนเช่นนี้:

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

เราสามารถทำให้รูปแบบแน่นขึ้นเช่นนี้:

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

แต่มันก็ยังบอบบางเกินไปและจะต้องมีการปรับแต่งเพิ่มเติมตามไฟล์ภายในระบบไฟล์ของคุณ ไม่ต้องพูดถึงเป็น@StephaneChazelesได้กล่าวถึงในความคิดเห็นที่ว่านี้สามารถ gamed grepเป็นธรรมได้อย่างง่ายดายโดยรวมของการขึ้นบรรทัดใหม่กับไฟล์ที่ชื่อเพื่อบายพาสรูปแบบข้างต้นเพื่อ

อ้างอิง

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


1
ฮาฉันอ่านหัวข้อเดียวกันและกำลังจะโพสต์ ฉันจะเพิ่มความพิเศษให้กับคุณแทน
slm

@slm คุณจะยินดีมากที่สุดเพื่อให้การเปลี่ยนแปลงใด ๆ :)
ราเมษ

2
อาจไม่ดีสำหรับการตรวจสอบเพราะอาจปลอมหรือซ่อนไฟล์ที่ไม่เปลี่ยนรูปได้โดยมีอักขระขึ้นบรรทัดใหม่ในชื่อไฟล์ด้วยวิธีการนั้น นอกจากนี้ไม่ใช่เรื่องแปลกที่ชื่อไฟล์จะมี-i-ชื่อ (มี 34 ในระบบที่ฉันเข้าสู่ระบบในปัจจุบัน) คุณอาจต้องการ-aตัวเลือกเช่นกัน
Stéphane Chazelas

1
จากความอยากรู้อยากเห็นอะไร+iในตัวอย่างแรก? มันไม่ทำงานสำหรับฉัน นอกจากนี้การ grepping สำหรับ-i-สันนิษฐานว่าแอตทริบิวต์ที่ปรากฏติดกับi(เช่นa) จะไม่มีการตั้งค่า
ทำให้หมดความสามารถ

1
ทำไมไม่ทำอย่างนั้น^....i? หรืออย่างน้อยสิ่งที่ชอบ^[^ ]*iถ้าiสามารถอยู่ในตำแหน่งอื่นนอกเหนือจากห้า
Ruslan

6

เนื่องจากวัตถุประสงค์ของสคริปต์คือการตรวจสอบเป็นสิ่งสำคัญอย่างยิ่งที่จะต้องจัดการอย่างถูกต้องกับชื่อไฟล์โดยพลการเช่นชื่อที่มีการขึ้นบรรทัดใหม่ สิ่งนี้ทำให้เป็นไปไม่ได้ที่จะใช้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_GETFLAGSioctl syscall และดึงไฟล์ธง inode นี่คือแนวคิดการพิสูจน์ของ 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 ในระบบของฉันคือFS_IOC_GETFLAGS 0x80046601
antonone

1
ค่าของขึ้นอยู่กับFS_IOC_GETFLAGS sizeof(long)ดูเช่นคำสั่งทุบตีต่อไปนี้เพื่อหาสิ่งที่ขยายตัวแมโครใน gcc -E - <<< $'#include <linux/fs.h>\nFS_IOC_GETFLAGS' | tail -n1C: ฉันได้จากนิพจน์ต่อไปนี้: (((2U) << (((0 +8)+8)+14)) | ((('f')) << (0 +8)) | (((1)) << 0) | ((((sizeof(long)))) << ((0 +8)+8)))ซึ่งทำให้ง่าย(2U << 30) | ('f' << 8) | 1 | (sizeof(long) << 16)ขึ้น
Ruslan

3

เพื่อจัดการกับชื่อไฟล์โดยพลการ (รวมทั้งตัวละครที่มีการขึ้นบรรทัดใหม่) ซึ่งเป็นเคล็ดลับปกติคือการหาไฟล์ภายในแทน.//. .เนื่องจาก//ปกติไม่สามารถเกิดขึ้นได้ขณะสำรวจทรีไดเรกทอรีคุณแน่ใจว่า//สัญญาณเริ่มต้นของชื่อไฟล์ใหม่ในเอาต์พุต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'เพื่อให้สามารถที่จะเลี้ยงมัน xargs -r0GNU

นอกจากนี้โปรดทราบว่า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 .ฟิกไดเร็กทอรีนั้นขึ้นอยู่กับเงื่อนไขการแข่งขัน หนึ่งสามารถทำให้มันลงไปยังไดเรกทอรีด้านนอกของต้นไม้ไดเรกทอรีเส้นทางที่.โดยการแทนที่บางไดเรกทอรีด้วย symlink ในเวลาที่เหมาะสม
  • แม้จะlsattr -d fileมีสภาพการแข่งขัน แอ็ตทริบิวต์เหล่านั้นใช้ได้กับไฟล์หรือไดเรกทอรีปกติเท่านั้น ดังนั้นlsattrการที่ไม่lstat()เป็นครั้งแรกเพื่อตรวจสอบว่าไฟล์ประเภทขวาแล้วไม่open()ตามมาด้วยioctl()การดึงคุณลักษณะ แต่มันจะโทรopen()โดยไม่มีO_NOFOLLOW(หรือ O_NOCTTY) บางคนสามารถแทนที่fileด้วย symlink เพื่อ/dev/watchdogยกตัวอย่างเช่นระหว่างlstat()และopen()และทำให้ระบบรีบูต มันควรจะopen(O_PATH|O_NOFOLLOW)ตามด้วยfstat(), openat()และioctl()ที่นี่เพื่อหลีกเลี่ยงสภาพการแข่งขัน

2

ขอบคุณ Ramesh, slm และStéphaneที่ชี้ให้ฉันไปในทิศทางที่ถูกต้อง (ฉันพลาด-Rสวิตช์ไปlsattr) น่าเสียดายที่ไม่มีคำตอบใดที่ทำงานได้อย่างถูกต้องสำหรับฉัน

ฉันมากับสิ่งต่อไปนี้:

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

สิ่งนี้จะช่วยป้องกันการขึ้นบรรทัดใหม่ที่ใช้ในการทำให้ไฟล์ปรากฏว่าไม่เปลี่ยนรูปเมื่อไม่ได้ใช้ มันไม่ได้ป้องกันไฟล์ที่ตั้งค่าไม่เปลี่ยนรูปและมีบรรทัดใหม่ในชื่อไฟล์ แต่เนื่องจากไฟล์ดังกล่าวจะต้องทำโดยรูทฉันจึงมั่นใจได้ว่าไฟล์ดังกล่าวไม่มีอยู่ในระบบไฟล์ของฉันสำหรับกรณีการใช้งานของฉัน (วิธีนี้ไม่เหมาะสำหรับการตรวจจับการบุกรุกในกรณีที่ผู้ใช้รูทอาจถูกโจมตี แต่ไม่ใช้lsattrยูทิลิตี้ของระบบเดียวกันซึ่งเป็นเจ้าของโดยผู้ใช้รูทรายเดียวกัน)


เฉพาะ root เท่านั้นที่สามารถเพิ่มบิตที่ไม่เปลี่ยนรูปได้ให้กับไฟล์ แต่ผู้ใช้รายอื่นสามารถเปลี่ยนชื่อองค์ประกอบของพา ธ ที่นำไปสู่ไฟล์เหล่านั้นในภายหลังดังนั้นไฟล์พา ธ อาจมีการขึ้นบรรทัดใหม่ นอกจากนี้ผู้ใช้สามารถสร้างเส้นทางไฟล์ (ไม่เปลี่ยนรูป) ที่จะหลอกสคริปต์ของคุณคิดว่าไฟล์อื่นไม่เปลี่ยนรูป
Stéphane Chazelas

2

การใช้find -execจะช้าเกินไปที่แยกการส่งออกของlsattrเป็นที่ไม่น่าเชื่อถือในทำนองเดียวกันกับที่lsใช้หลามในขณะที่คำตอบโดย Gillesต้องเลือกอย่างต่อเนื่องสำหรับการioctlขึ้นอยู่กับว่าล่ามงูหลามเป็น 32 หรือ 64 บิต ...

ปัญหาที่เกิดขึ้นอยู่ในระดับต่ำหรือต่ำดังนั้นให้ลงไปที่ระดับล่าง: C ++ ไม่เลวเท่าภาษาสคริปต์ :) เนื่องจากเป็นโบนัสมันสามารถเข้าถึงส่วนหัวของระบบ C ที่มีพรีโพรเซสเซอร์ C แบบเต็ม

โปรแกรมต่อไปนี้ค้นหาไฟล์ที่ไม่เปลี่ยนรูปอยู่ภายในระบบไฟล์เดียวนั่นคือไม่เคยข้ามจุดเมานต์ ในการค้นหาต้นไม้ที่เห็นได้ชัดให้ข้ามจุดเมานท์ตามต้องการลบการFTW_MOUNTตั้งค่าสถานะในการnftwโทร นอกจากนี้ยังไม่ได้ติดตาม symlinks หากต้องการติดตามพวกเขาให้ลบการ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;
    }
}

-1

แทนที่จะไพพ์เอาต์พุตเป็น 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

ข้อมูลโค้ดแรกของคุณมีตัวพิมพ์ผิดและตัวที่สองไม่พบไฟล์ที่ไม่เปลี่ยนรูปในระบบของฉัน
ลดลง

แก้ไขการพิมพ์ผิดในคำสั่งแรก บางทีไฟล์ที่สองไม่สามารถหาไฟล์ที่ไม่เปลี่ยนรูปแบบได้เพราะยังไม่มี?
Rootdev

/etcผมไม่ได้สังเกตคำสั่งที่สองเป็นเพียงการมองใน แต่คำสั่งทั้งคู่พบไฟล์ที่ไม่เปลี่ยนรูปแบบไม่ถูกต้องซึ่งสร้างด้วยtouch `"echo -e "bogus\n---------i---e-- changeable"`"
depquid

มีข้อความระบุว่าในโพสต์ดั้งเดิมของฉันที่ฉันเรียกใช้ผ่าน cron เพื่อสแกนไดเรกทอรี / etc ฉันไม่สามารถช่วยได้ถ้าคุณไม่ได้อ่านโพสต์หรือคำสั่งก่อนที่จะเรียกใช้ และใช่คุณสามารถสร้างเคสเพื่อหลอกการค้นหาใด ๆ ถ้าคุณรู้สึกว่ามัน แต่เนื่องจากคุณรวดเร็วมากที่จะชี้ให้เห็นการพิมพ์ผิดในคำสั่งเดิมของฉัน (หายไปล่าสุด ') ฉันจะชี้ให้เห็นว่า คำสั่งของคุณไม่ทำงานตามที่เขียนดังนั้นจะไม่สร้างอะไรขึ้น! :-)
Rootdev

ความผิดฉันเอง. ลองสิ่งนี้:touch "`echo -e 'bogus\n---------i---e-- changeable'`"
ทำให้หมดกำลังใจ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.