Działa na OpenBSD
Jak już wspomniano w komentarzu @eradman, jest to możliwe na OpenBSD.
Jako root:
hzy# cat <<'EOT' >/tmp/foo; chmod 001 /tmp/foo
#! /bin/sh
: this is secret
echo done
EOT
Jako zwykły użytkownik:
hzy$ cat /tmp/foo
cat: /tmp/foo: Permission denied
hzy$ /tmp/foo
done
Działa to poprzez przekazanie /dev/fd/3
(lub cokolwiek innego otwartego fd do skryptu) do tłumacza. Ta sztuczka nie działałaby w systemie Linux, gdzie /dev/fd/N
nie są specjalne urządzenia znakowe, które zwracają a dup(2)
fd po otwarciu, ale „magiczne” dowiązania symboliczne do oryginalnego pliku / dentry, które otwierają plik od zera [1]. To mogłoby być realizowane Wolny / NetBSD lub Solaris ...
Ale to nie jest to, czym się spieprzyło
Zasadniczo udzielenie uprawnienia x
(wykonania) oznacza również udzielenie uprawnienia r
(odczytu) do dowolnego pliku, który ma 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
to nie jedyny sposób; jeśli interpreter jest dynamicznie połączony z plikiem wykonywalnym, takim jak perl
lub python
, można zamiast tego użyć LD_PRELOAD
ed hacka, który przesłania read(2)
funkcję.
I nie, ustawienie setuid nie uniemożliwi zwykłemu użytkownikowi oglądania jego zawartości; mogłaby po prostu go uruchomić ptrace(2)
, co spowoduje ignorowanie bitów setuid:
Jako root:
hzyS# cat <<'EOT' >/tmp/bar; chmod 4001 /tmp/bar
#! /bin/sh
: this is secret
id
EOT
Jako zwykły użytkownik:
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
"
(przepraszam, jeśli nie jest to najłatwiejszy sposób na wykazanie tego)
[1] można to emulować w systemie Linux za pomocą binfmt_misc
, ale interpreter będzie musiał zostać zmodyfikowany lub będzie konieczne użycie opakowania; patrz ostatnia część tej odpowiedzi na przykład celowo uczyniony absurdalnie niepewnym.
[2] lub ogólnie dowolny plik, który nie spowoduje execve()
zwrotu ENOEXEC
.