Funziona su OpenBSD
Come già accennato in un commento di @eradman, questo è possibile su OpenBSD.
Come root:
hzy# cat <<'EOT' >/tmp/foo; chmod 001 /tmp/foo
#! /bin/sh
: this is secret
echo done
EOT
Come utente normale:
hzy$ cat /tmp/foo
cat: /tmp/foo: Permission denied
hzy$ /tmp/foo
done
Funziona passando /dev/fd/3
(o qualunque sia la fd aperta allo script) all'interprete. Quel trucco non funzionerebbe su Linux, dove /dev/fd/N
non sono dispositivi a caratteri speciali che restituiscono una dup(2)
delle fd quando vengono aperti, ma collegamenti simbolici "magici" al file / dentry originale, che aprono il file da zero [1]. Si potrebbe essere implementato in libero / NetBSD o Solaris ...
Ma non è quello che è crackato per essere
Fondamentalmente dare il x
permesso (di esecuzione) significa anche dare il r
permesso (di lettura) su qualsiasi file che ha un 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
non è l'unico modo; se l'interprete è eseguibile dinamicamente collegato come perl
o python
, si potrebbe usare invece un LD_PRELOAD
hack ed che sovrascrive la read(2)
funzione.
E no, renderlo setuid non impedirà a un utente normale di vederne il contenuto; potrebbe semplicemente eseguirlo ptrace(2)
, il che farà ignorare i bit setuid:
Come root:
hzyS# cat <<'EOT' >/tmp/bar; chmod 4001 /tmp/bar
#! /bin/sh
: this is secret
id
EOT
Come utente normale:
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
"
(scusate se questo non è il modo più diretto per dimostrarlo)
[1] questo potrebbe essere emulato su Linux usando binfmt_misc
, ma l'interprete dovrà essere modificato, o dovrà essere usato un wrapper; vedi l'ultima parte di questa risposta per un esempio reso deliberatamente ridicolmente insicuro.
[2] o in generale, qualsiasi file che non causerà la execve()
restituzione ENOEXEC
.