Funciona no OpenBSD
Como já mencionado em um comentário de @eradman, isso é possível no OpenBSD.
Como raiz:
hzy# cat <<'EOT' >/tmp/foo; chmod 001 /tmp/foo
#! /bin/sh
: this is secret
echo done
EOT
Como usuário regular:
hzy$ cat /tmp/foo
cat: /tmp/foo: Permission denied
hzy$ /tmp/foo
done
Isso funciona passando /dev/fd/3
(ou seja o que for o código aberto para o script) para o intérprete. Esse truque não funcionaria no Linux, onde /dev/fd/N
não há dispositivos de caracteres especiais que retornam um valor dup(2)
de fd quando abertos, mas links simbólicos "mágicos" para o arquivo / dentry original, que abrem o arquivo do zero [1]. Ele poderia ser implementado no Free / NetBSD ou Solaris ...
Mas não é o que parece ser
Basicamente, conceder a x
permissão (executar) significa também conceder a r
permissão (ler) em qualquer arquivo que tenha um shebang [2]:
hzy$ cat /tmp/foo
cat: /tmp/foo: Permission denied
hzy$ ktrace -ti /tmp/foo
done
hzy$ kdump | tail -n8
70154 sh GIO fd 10 read 38 bytes
"#! /bin/sh
: this is secret
echo done
"
70154 sh GIO fd 1 wrote 5 bytes
"done
ktrace
não é o único caminho; se o intérprete for executável dinamicamente vinculado como perl
ou python
, um LD_PRELOAD
hack ed que substitui a read(2)
função pode ser usado.
E não, torná-lo setuid não impedirá que um usuário comum veja seu conteúdo; ela poderia simplesmente executá-lo ptrace(2)
, o que fará com que os bits setuid sejam ignorados:
Como raiz:
hzyS# cat <<'EOT' >/tmp/bar; chmod 4001 /tmp/bar
#! /bin/sh
: this is secret
id
EOT
Como usuário regular:
hzyS$ ktrace -ti /tmp/bar
uid=1001(duns) euid=0(root) gid=1001(duns) groups=1001(duns)
hzyS$ kdump
... nothing, the kernel disabled the ktrace ...
hzyS$ cc -Wall -xc - -o pt <<'EOT'
#include <unistd.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <signal.h>
int main(int ac, char **av){
int s; pid_t pid;
if((pid = fork()) == 0){
ptrace(PT_TRACE_ME, 0, 0, 0);
execvp(av[1], av + 1);
}
while(wait(&s) > 0 && WIFSTOPPED(s)){
s = WSTOPSIG(s);
ptrace(PT_CONTINUE, pid, (caddr_t)1, s == SIGTRAP ? 0 : s);
}
}
EOT
hzyS$ ./pt ktrace -ti /tmp/bar
uid=1001(duns) gid=1001(duns) groups=1001(duns)
hzyS$ kdump | tail -5
29543 sh GIO fd 10 read 31 bytes
"#! /bin/sh
: this is secret
id
"
(desculpe se esta não é a maneira mais direta de demonstrá-lo)
[1] isso pode ser emulado no Linux usando binfmt_misc
, mas o intérprete terá que ser modificado ou um wrapper terá que ser usado; veja a última parte desta resposta para um exemplo deliberadamente ridiculamente inseguro.
[2] ou, em geral, qualquer arquivo que não cause execve()
retorno ENOEXEC
.