Monitorando o programa chama um arquivo executável

4

Eu quero saber qual programa chama um executável específico, incluindo quando esse executável é usado como um intérprete por meio de uma linha shebang.

Este não é exatamente o mesmo problema que saber qual programa acessa um determinado arquivo . Por exemplo, auditctl -w /usr/bin/myprogram me diz que o programa está sendo executado por ... próprio, já que o evento de auditoria é gerado após a chamada execve bem-sucedida.

Uma opção é substituir o executável por um programa wrapper, como este…

#!/bin/sh
logger "$0: executed by uid=$(id -u) ruid=$(id -ur) cmd=$(ps -o args= -p $PPID)"
exec "$0.real" "$@"

Mas isso requer a movimentação do arquivo real, que é disruptivo (o arquivo não pode ser somente leitura, choca-se com modificações feitas por um gerenciador de pacotes etc.). E não funciona se o programa for usado como um interpretador para um script, porque shebang não se aninha. (Nesse caso, auditctl -w /usr/bin/interpreter fornece um resultado útil, mas eu quero uma solução que funcione para ambos os casos.) Ele também não funciona para programas setuid se /bin/sh for bash desde que o bash descarta privilégios.

Como posso monitorar execuções de um determinado executável incluindo os usos do executável como um interpretador shebang e, em particular, registrar informações úteis sobre o processo de chamada (não apenas o PPID, mas pelo menos o nome do processo ou caminho executável pai? também o usuário invocando e argumentos)? De preferência sem substituir o arquivo por um invólucro. Uma solução específica para Linux está bem.

    
por Gilles 22.08.2016 / 01:46

2 respostas

5

Isso seria hacky, mas se fosse um executável vinculado dinamicamente, você poderia configurar um pré-carregamento global em /etc/ld.so.preload , o que acionaria apenas um gancho de log se detectasse que você estava no executável correto.

Algo como:

#define _XOPEN_SOURCE
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#define TARGET "/some_executable"

__attribute__((constructor)) 
static void 
logger(int argc, char** argv){ 
    /*catch own argv right here and parent's later from /proc */

    static char buf[sizeof(TARGET)];

    readlink("/proc/self/exe", buf, sizeof(buf)-1);

    if ( 0==strcmp(TARGET, buf)){
        /* ... */
        syslog(/*...*/);
    }
}

A desvantagem óbvia desta abordagem é que ela atrasa um pouco a execução de cada executável vinculado dinamicamente em seu sistema, mas minhas medições indicam que o atraso é bem pequeno (< 1ms em que fork + exec custa cerca de 2ms).

Quanto ao problema de permissão removida, você pode ter um pequeno binário setuid-root que lerá e ecoará incondicionalmente seus arquivos proc dos avós (o arquivo status , provavelmente), possivelmente se e somente se seu pai for o executável cujos pais você deseja registrar. Você poderia, então, gerar esse executável setuid dentro de seu gancho de logging para obter as informações sobre o pai executável (avô do auxiliar setuid).

    
por 22.08.2016 / 02:35
4

Você pode usar a API fanotify para ouvir todas as aberturas feitas em arquivos em um determinado sistema de arquivos. As informações são fornecidas como um fluxo de estruturas de eventos que incluem um descritor de arquivo que fornece o nome do arquivo sendo aberto e o ID do processo do solicitante.

Seu programa pode optar por permitir ou rejeitar o aberto, o que significa que ele pode procurar o pid em /proc para encontrar o comando e o usuário fazendo a abertura, antes de permitir que continue, sem possibilidade de uma condição de corrida. / p>

A página man fanotify (7) fornece um programa C completo para obter eventos e interceda nas solicitações abertas, portanto, basta inserir algumas linhas extras para obter os dados desejados de /proc . Observe que você obterá eventos para todos os arquivos em um ponto de montagem inteiro, portanto, teste seu código em um sistema de arquivos pequeno especialmente montado.

O comando fatrace mostra como você obtém algumas dessas informações em um teste simples. Por exemplo, copie /bin/bash para /tmp/bash e, em seguida, escreva um pequeno script ~/test2 com

#!/tmp/bash
pwd

e execute fatrace apenas no sistema de arquivos /tmp (supondo que seja uma montagem tmpfs separada) usando a opção -c para ouvir o sistema de arquivos do diretório atual e -f O para atender a abertura:

cd /tmp
sudo fatrace -c -f O

Agora, quando você corre

sh -c 'echo $$; ~/test2'
expect -c 'spawn /home/meuh/test2'
~/test2

você deve ver logado

sh(7360): RO /tmp/bash
expect(7414): RO /tmp/bash
bash(7590): RO /tmp/bash
    
por 22.08.2016 / 12:23