Como procurar arquivos com conjunto de atributos imutáveis?

16

Por razões de auditoria de configuração, eu quero ser capaz de procurar no meu sistema de arquivos ext3 por arquivos que possuem o atributo imutável definido (via chattr +i ). Não consigo encontrar nenhuma opção para find ou similar que faça isso. Neste ponto, tenho medo de ter que escrever meu próprio script para analisar lsattr output para cada diretório. Existe um utilitário padrão que fornece uma maneira melhor?

    
por depquid 31.05.2014 / 04:34

7 respostas

2

Obrigado a Ramesh, slm e Stéphane por me apontar na direção certa (estava faltando a opção -R para lsattr ). Infelizmente, nenhuma das respostas funcionou corretamente para mim.

Eu fiz o seguinte:

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

Isso protege contra novas linhas sendo usadas para fazer um arquivo parecer imutável quando não é. Não protege contra arquivos que são imutáveis e tem novas linhas em seus nomes de arquivos. Mas como esse arquivo teria que ser feito dessa maneira pelo root, posso ter certeza de que esses arquivos não existem no meu sistema de arquivos para o meu caso de uso. (Esse método não é adequado para detecção de invasão em casos em que o usuário raiz pode estar comprometido, mas também não está usando o mesmo utilitário lsattr do sistema, que também é de propriedade do mesmo usuário raiz.)

    
por 12.06.2014 / 22:23
9

Isso pode ser parcialmente realizado canalizando o comando grep para o comando lsattr .

lsattr -R | grep +i

No entanto, acredito que quando você menciona todo o sistema de arquivos ext3 , a pesquisa pode envolver /proc , /dev e alguns outros diretórios que, se relatar algum erro, você só deseja ignorar. Você provavelmente pode executar o comando como

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

Você pode tornar o grep um pouco mais restrito usando o recurso PCRE de grep para corresponder mais explicitamente ao "-i-".

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

Isso funcionará para situações como esta:

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

Mas é imperfeito. Se houver atributos adicionais ativados em torno do sinalizador imutável, não os igualaremos, e isso será enganado por arquivos cujos nomes também correspondem ao padrão acima, como este:

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

Podemos melhorar o padrão um pouco mais assim:

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

Mas ainda é um pouco frágil e exigiria ajustes adicionais, dependendo dos arquivos dentro do seu sistema de arquivos. Sem mencionar como @StephaneChazeles mencionou nos comentários que isso pode ser feito com bastante facilidade pela inclusão de novas linhas com um nome de arquivo para ignorar o padrão acima para grep .

Referências

link

    
por 31.05.2014 / 05:15
6

Dado que a finalidade do script é a auditoria, é especialmente importante lidar corretamente com nomes de arquivos arbitrários, por exemplo, com nomes contendo novas linhas. Isso torna impossível usar lsattr em vários arquivos simultaneamente, pois a saída de lsattr pode ser ambígua nesse caso.

Você pode recurse com find e chamar lsattr em um arquivo de cada vez. Vai ser bem lento.

find / -xdev -exec sh -c '
  for i do
     attrs=$(lsattr -d "$i"); attrs=${attrs%% *}
     case $attrs in
       *i*) printf "%s
#!/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 + '
find / -xdev -exec sh -c '
  for i do
     attrs=$(lsattr -d "$i"); attrs=${attrs%% *}
     case $attrs in
       *i*) printf "%s
#!/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 + '%pre%')
        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)
" "$i";; esac done' sh {} +
') 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)
" "$i";; esac done' sh {} +

Eu recomendo usar uma linguagem menos irritada, como Perl, Python ou Ruby, e fazer o trabalho de lsattr por você mesmo. lsattr opera emitindo um syscall de FS_IOC_GETFLAGS ioctl e recuperando os flags de inode do arquivo . Aqui está uma prova de conceito do Python.

%pre%     
por 02.06.2014 / 03:24
3

Para lidar com nomes de arquivos arbitrários (incluindo os que contêm caracteres de nova linha), o truque comum é encontrar arquivos dentro de .//. em vez de . . Como // não pode ocorrer normalmente ao percorrer a árvore de diretórios, você tem certeza de que // sinaliza o início de um novo nome de arquivo na saída find (ou aqui 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()}'

Observe que a saída ainda será separada por nova linha. Se você precisar pós-processá-lo, terá que adaptá-lo. Por exemplo, você poderia adicionar um -v ORS='xargs -r0' para poder alimentá-lo com lsattr -R do GNU.

Observe também que find (pelo menos 1.42.13) não pode reportar os flags de arquivos cujo caminho é maior que PATH_MAX (geralmente 4096), então alguém poderia ocultar um arquivo imutável, movendo seu diretório pai (ou qualquer um dos componentes do caminho que levam a ele, exceto ele próprio como imutável) para um diretório muito profundo.

Uma solução seria usar -execdir com -print0 :

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

Agora, com lsattr , isso é pós-processável, mas se você pretende fazer algo com esses caminhos, observe que qualquer chamada de sistema em caminhos de arquivo maiores que PATH_MAX ainda falhará e componentes de diretório poderia ter sido renomeado no intervalo.

Se quisermos obter um relatório confiável em uma árvore de diretórios potencialmente gravável por outras pessoas, há mais alguns problemas inerentes ao comando lsattr -R . que precisaríamos mencionar:

  • o caminho . atravessa a árvore de diretórios, está sujeito a condições de corrida. Pode-se fazer descer para diretórios fora da árvore de diretórios roteados em lsattr -d file , substituindo alguns diretórios por links simbólicos no momento certo.
  • até lsattr tem uma condição de corrida. Esses atributos são aplicáveis apenas a arquivos ou diretórios comuns. Portanto, lstat() faz um open() primeiro para verificar se o arquivo é dos tipos corretos e então ioctl() seguido por open() para recuperar os atributos. Mas chama O_NOFOLLOW sem file (nem O_NOCTTY). Alguém pode substituir /dev/watchdog por um link simbólico para lstat() , por exemplo, entre open() e open(O_PATH|O_NOFOLLOW) e fazer com que o sistema seja reinicializado. Ele deve fazer fstat() seguido por openat() , ioctl() e %code% aqui para evitar as condições de corrida.
por 03.06.2014 / 16:39
2

Usar find -exec é muito lento, a análise de saída de lsattr é não confiável semelhante à de ls , usando o Python como na resposta de Gilles , precisa escolher a constante para ioctl , dependendo se o O interpretador Python é de 32 ou 64 bits ...

O problema em questão é mais ou menos baixo nível, então vamos para o nível inferior: C ++ não é tão ruim quanto uma linguagem de script :) Como bônus, ele tem acesso aos cabeçalhos do sistema C com o poder total do pré-processador C .

O programa a seguir procura por arquivos imutáveis, permanecendo dentro de um sistema de arquivos, ou seja, nunca cruza pontos de montagem. Para pesquisar a árvore aparente, cruzando os pontos de montagem conforme necessário, remova o sinal FTW_MOUNT na chamada nftw . Também não segue links simbólicos. Para segui-los, remova o sinal 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;
    }
}
    
por 24.06.2017 / 17:32
-1

Em vez de direcionar a saída para o grep, por que não usar apenas o awk para corresponder apenas ao 'i' no primeiro campo da saída?

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

Na verdade, eu corro isso diariamente através do cron para escanear o diretório / etc em centenas de servidores e enviar a saída para o syslog. Eu posso então gerar um relatório diário via Splunk:

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

Muito simples, vá para a pasta suspeita e execute o comando:

lsattr -laR | grep *immutable
    
por 01.11.2017 / 20:05

Tags