web-dev-qa-db-fra.com

Un script peut-il être exécutable mais non lisible?

Est-il possible d'exécuter un script s'il n'y a pas d'autorisation pour le lire? En mode root, j'ai fait un script et je veux que l'autre utilisateur exécute ce script mais ne le lise pas. J'ai fait chmod pour interdire la lecture et l'écriture mais permettre l'exécution, cependant en mode utilisateur, j'ai vu le message qui dit: permission refusée.

69
ashim

Le problème est que le script n'est pas ce qui est en cours d'exécution, mais l'interpréteur (bash, Perl, python, etc.). Et l'interprète doit lire le script. Ceci est différent d'un programme "normal", comme ls, en ce que le programme est chargé directement dans le noyau, comme le ferait l'interpréteur. Puisque le noyau lui-même lit le fichier programme, il n'a pas à se soucier de l'accès en lecture. L'interpréteur doit lire le fichier de script, car un fichier normal devrait être lu.

70
Arcege

Ceci n'est possible que pour les binaires.

$ chown foo:foo bar
$ chmod 701 bar

En tant qu'utilisateur non privilégié:

$ ls -lha bar
-rwx-----x 1 foo foo 7.0K 2012-03-15 03:06 bar

$ cat bar
cat: bar: Permission denied

$ ./bar
baz

Maintenant, voici le kicker. Bien que le fichier soit illisible par des moyens conventionnels, vous ne pouvez pas réellement empêcher la lecture du fichier. C'est en fait un défi sur http://smashthestack.org/ (niveau 13). Il existe un utilitaire bien connu appelé hktrace qui vous permet de lire le fichier à l'aide de ptrace.

35
kwarrick

Ce n'est pas possible, au moins sous Linux (d'autres Unices pourraient le permettre); pensez-y, lorsque vous exécutez le script, le shell doit le lire pour savoir quoi faire.

6
Renan

Il y a une demi-vérité dans les déclarations précédentes. Vous pouvez configurer un script afin qu'il ne soit pas lisible par l'utilisateur, mais qu'il soit toujours exécutable. Le processus est un peu long, mais il est possible en faisant une exception dans/etc/sudoer afin que l'utilisateur puisse exécuter le script en tant que vous-même temporairement sans être invité à entrer un mot de passe. Cette méthode: - contourne le patch setuid pour les autres distributions. - vous permet d'accorder temporairement des autorisations élevées pour un script spécifique sans donner à l'utilisateur Sudo des droits sur tout.

Suivez les instructions sur ce post: autorisation de fichier exécuter uniquement

3
santana

Vous pouvez, je pense, le faire avec setuid.

Sauf que vous ne pouvez pas parce que la plupart des distributions (apparemment) ont setuid désactivées parce que c'est une faille de sécurité massive. Elle est désactivée sur la mienne, donc je ne sais pas vraiment si cette réponse fonctionnera, je la poste quand même parce que je pense qu'elle devrait.

Quoi qu'il en soit, si je voulais faire ce que vous vouliez faire - et j'avais une distribution avec setuid activé pour les scripts - je ferais quelque chose comme:

$ chmod 700 myscript
$ cat > myscript-nonroot
#!/bin/sh
bash myscript
^D
$ Sudo chown root:root myscript-nonroot
$ Sudo chmod 4755 myscript-nonroot # make SURE this isn't world-writable!

C'est-à-dire que j'écrirais un autre script dont le seul but est d'appeler le script root en lecture seule, de le changer pour qu'il appartienne à root et de lui donner l'autorisation setuid. (Avec le statut non accessible en écriture de tout le monde.)

Étant donné que la fonction myscript-nonroot est lisible par tout le monde, elle peut être lue et exécutée, et au moment où vous obtenez deux la ligne où vous exécutez réellement votre script (bash myscript) il est exécuté en tant que root (ou qui que ce soit d'autre, l'utilisateur exact n'a pas d'importance, tant que le fichier wrapper appartient au même utilisateur.)

3
quodlibetor

Dans cette situation, j'ai utilisé Sudo avec une option NOPASSWD pour que les utilisateurs puissent exécuter le script sans pouvoir le lire.

2
user33577

Cela fonctionne sur OpenBSD

Comme déjà mentionné dans un commentaire par @eradman, cela est possible sur OpenBSD.

En tant que root:

hzy# cat <<'EOT' >/tmp/foo; chmod 001 /tmp/foo
#! /bin/sh
: this is secret
echo done
EOT

En tant qu'utilisateur régulier:

hzy$ cat /tmp/foo
cat: /tmp/foo: Permission denied
hzy$ /tmp/foo
done

Cela fonctionne en passant /dev/fd/3 (Ou quel que soit le fd ouvert au script) à l'interpréteur. Cette astuce ne fonctionnerait pas sous Linux, où /dev/fd/N Ne sont pas des périphériques de caractères spéciaux qui renvoient une dup(2) du fd lorsqu'il est ouvert, mais des liens symboliques "magiques" vers le fichier/dentry d'origine, qui ouvrent le fichier à partir de zéro [1]. Il pourrait être implémenté dans Free/NetBSD ou Solaris ...

Mais ce n'est pas ce que c'est

En gros, donner l'autorisation x (exécuter) signifie également donner l'autorisation r (lire) sur tout fichier qui a 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 n'est pas le seul moyen; si l'interpréteur est un exécutable lié dynamiquement comme Perl ou python, un hack LD_PRELOAD ed qui remplace la fonction read(2) pourrait être utilisé à la place.

Et non, le rendre setuid n'empêchera pas un utilisateur régulier de voir son contenu; elle pourrait simplement l'exécuter sous ptrace(2), ce qui entraînera l'ignorance des bits setuid:

En tant que root:

hzyS# cat <<'EOT' >/tmp/bar; chmod 4001 /tmp/bar
#! /bin/sh
: this is secret
id
EOT

En tant qu'utilisateur régulier:

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
       "

(désolé si ce n'est pas la façon la plus simple de le démontrer)

[1] cela pourrait être émulé sous Linux en utilisant binfmt_misc, Mais l'interpréteur devra être modifié, ou un wrapper devra être utilisé; voir la dernière partie de cette réponse pour un exemple délibérément rendu ridiculement précaire.

[2] ou en général, tout fichier qui n'entraînera pas le retour de execve()ENOEXEC.

0
mosvy