Comment forcer explicitement et en toute sécurité l'utilisation d'une commande intégrée dans bash

19

Il y a une question similaire qui traite du scénario de «wrapping», où vous souhaitez remplacer par exemple cdune commande qui appelle la fonction intégrée cd.

Cependant, à la lumière de shellshock et al et sachant que bash importe des fonctions de l'environnement, j'ai fait quelques tests et je ne peux pas trouver un moyen d'appeler le builtin en toute sécurité à cdpartir de mon script.

Considère ceci

cd() { echo "muahaha"; }
export -f cd

Tous les scripts appelés dans cet environnement à l'aide de cdse briser (considérer les effets de quelque chose comme cd dir && rm -rf .).

Il existe des commandes pour vérifier le type d'une commande (appelée commodément type) et des commandes pour exécuter la version intégrée plutôt qu'une fonction ( builtinet command). Mais, voilà, ceux-ci peuvent également être remplacés à l'aide de fonctions

builtin() { "$@"; }
command() { "$@"; }
type() { echo "$1 is a shell builtin"; }

Donnera ce qui suit:

$ type cd
cd is a shell builtin
$ cd x
muahaha
$ builtin cd x
muahaha
$ command cd x
muahaha

Existe-t-il un moyen de forcer en toute sécurité bash à utiliser la commande intégrée, ou au moins détecter qu'une commande n'est pas une commande intégrée, sans effacer tout l'environnement?

Je me rends compte que si quelqu'un contrôle votre environnement, vous êtes probablement foutu de toute façon, mais au moins pour les alias, vous avez la possibilité de ne pas appeler l'alias en insérant un \avant.

falstro
la source
vous pouvez exécuter votre script en utilisant la envcommande avant, comme ceci:env -i <SCRIPT.sh>
Arkadiusz Drabczyk
Seulement si envn'est pas redéfini en tant que fonction également. C'est terrifiant. J'ai d'abord pensé que les caractères spéciaux aideraient - appeler avec un chemin complet qui inclut /, utiliser .pour source, et ainsi de suite. Mais ceux-ci peuvent également être utilisés pour les noms de fonction! Vous pouvez redéfinir n'importe quelle fonction que vous souhaitez, mais il est difficile de revenir à l'appel de la commande d'origine.
orion
1
Certes, mais si vous voulez exécuter un script sur une machine compromise, vous êtes quand même foutu. Sinon, écrivez votre script #/bin/shs'il ne s'agit pas d'un shell interactif par défaut.
Arkadiusz Drabczyk

Réponses:

16

Olivier D est presque correct, mais vous devez vous mettre POSIXLY_CORRECT=1avant de courir unset. POSIX a une notion de Special Built-ins , et bash la prend en charge . unsetest un tel intégré. Rechercher SPECIAL_BUILTINdans builtins/*.cla source de bash pour une liste, il comprend set, unset, export, evalet source.

$ unset() { echo muahaha-unset; }
$ unset unset
muahaha-unset
$ POSIXLY_CORRECT=1
$ unset unset

Le voleur unseta été retiré de l'environnement, si vous enlevez command, type, builtinalors vous devriez être en mesure de procéder, maisunset POSIXLY_CORRECT si vous comptez sur le comportement non-POSIX ou fonctionnalités avancées de bash.

Cela ne concerne pas les alias, vous devez donc utiliser \unsetpour être sûr que cela fonctionne dans le shell interactif (ou toujours, si le cas expand_aliasesest en vigueur).

Pour les paranoïaques, cela devrait tout arranger, je pense:

POSIXLY_CORRECT=1
\unset -f help read unset
\unset POSIXLY_CORRECT
re='^([a-z:.\[]+):' # =~ is troublesome to escape
while \read cmd; do 
    [[ "$cmd" =~ $re ]] && \unset -f ${BASH_REMATCH[1]}; 
done < <( \help -s "*" )

( while, do, doneEt [[sont des mots réservés et ne précautions ont pas besoin.) Notez que nous utilisons unset -fpour être sûr de fonctions non définies, bien que les variables et les fonctions partagent le même espace , il est possible pour que les deux existent simultanément (grâce à Etan Reisner) auquel cas Unset-ing deux fois ferait aussi l'affaire. Vous pouvez marquer une fonction en lecture seule, bash ne vous empêche pas de désactiver une fonction en lecture seule jusqu'à bash-4.2 inclus, bash-4.3 vous en empêche, mais il honore toujours les commandes spéciales lorsquePOSIXLY_CORRECT est défini.

Une lecture seule POSIXLY_CORRECTn'est pas un vrai problème, ce n'est pas un booléen ou un indicateur sa présence active le mode POSIX, donc s'il existe en lecture seule, vous pouvez compter sur les fonctionnalités POSIX, même si la valeur est vide ou 0. Vous aurez simplement besoin de fonctions problématiques non définies d'une manière différente de celle ci-dessus, peut-être avec un copier-coller:

\help -s "*" | while IFS=": " read cmd junk; do echo \\unset -f $cmd; done

(et ignorez toutes les erreurs) ou engagez-vous dans d'autres scripts .


Autres notes:

  • functionest un mot réservé, il peut être aliasé mais pas remplacé par une fonction. (L'aliasing functionest légèrement gênant car il \function n'est pas acceptable comme moyen de le contourner)
  • [[, ]]sont des mots réservés, ils peuvent être aliasés (qui seront ignorés) mais pas remplacés par une fonction (bien que les fonctions puissent être ainsi nommées)
  • (( n'est pas un nom valide pour une fonction, ni un alias
Mr Spuratic
la source
Intéressant, merci! Il me semble étrange que bash ne protège pas par défaut unsetet al d'être écrasé.
falstro
Cela a besoin de l' -fargument, unsetn'est-ce pas? Sinon, si une variable et une fonction nommées de la même manière qu'une fonction intégrée sont définies unset, la variable à désélectionner sera sélectionnée en premier. Cela échoue également si l'une des fonctions d'obscurcissement est définie, readonlyn'est-ce pas? (Bien que cela soit détectable et puisse être une erreur fatale.) Cela manque également la fonction [intégrée, semble-t-il.
Etan Reisner
1
Je ne pense pas que \unset -f unsetcela ait du sens, étant donné que cela se fera dans la boucle de lecture. Si unsetest une fonction que vous \unsety résoudriez normalement au lieu de la fonction intégrée, mais vous pouvez l'utiliser en \unsettoute sécurité tant que le mode POSIX est en vigueur. Explicitement appeler \unset -fsur [, .et :peut - être une bonne idée cependant, que vos exclut regex eux. J'ajouterais également -fà \unsetdans la boucle, et ferais \unset POSIXLY_CORRECTaprès la boucle, au lieu d'avant. \unalias -a(après \unset -f unalias) permet également de renoncer en toute sécurité à l'échappement lors des commandes suivantes.
Adrian Günter
1
@ AdrianGünter Try:, [[ ${POSIXLY_CORRECT?non-POSIX mode shell is untrusted}x ]]cela provoquera la fermeture d' un script non interactif avec l'erreur indiquée si la variable n'est pas définie, ou continuera si elle est définie (vide ou n'importe quelle valeur).
mr.spuratic
1
"vous devez utiliser \unset" ... Il convient de noter que la citation de tout caractère fonctionnerait pour éviter l'expansion d'alias, pas seulement le premier. un"se"tfonctionnerait tout aussi bien, mais moins lisible. "Le premier mot de chaque commande simple, s'il n'est pas cité, est vérifié pour voir s'il a un alias." - Bash Ref
Robin A. Meade
6

Je me rends compte que si quelqu'un contrôle votre environnement, vous êtes probablement foutu de toute façon

Oui ça. Si vous exécutez un script dans un environnement inconnu, toutes sortes de choses peuvent mal se passer, en commençant par LD_PRELOADfaire en sorte que le processus shell exécute du code arbitraire avant même de lire votre script. Tenter de se protéger contre un environnement hostile depuis l'intérieur du script est futile.

Sudo a assaini l'environnement en supprimant tout ce qui ressemble à une définition de fonction bash depuis plus d'une décennie. Depuis Shellshock , d'autres environnements qui exécutent des scripts shell dans un environnement non entièrement fiable ont emboîté le pas.

Vous ne pouvez pas exécuter un script en toute sécurité dans un environnement qui a été défini par une entité non approuvée. S'inquiéter des définitions de fonction n'est donc pas productif. Assainissez votre environnement et, ce faisant, les variables que bash interpréterait comme des définitions de fonction.

Gilles 'SO- arrête d'être méchant'
la source
1
Je suis entièrement en désaccord avec cela. La sécurité n'est pas une proposition binaire centrée sur "assaini" ou "non assaini". Il s'agit de supprimer les opportunités d'exploitation. Et malheureusement, la complexité des systèmes modernes signifie qu'il existe de nombreuses possibilités d'exploitation qui doivent être verrouillées correctement. De nombreuses opportunités d'exploitation se concentrent sur des attaques en amont d'apparence anodine, telles que la modification de la variable PATH, la redéfinition des fonctions intégrées, etc.
Dejay Clayton
@DejayClayton Je suis entièrement d'accord avec votre commentaire, à l'exception de la première phrase, car je ne vois pas en quoi cela contredit ma réponse. La suppression d'une opportunité d'exploitation est utile, mais pas la suppression de la moitié seulement lorsqu'une légère modification de l'attaque lui permet de continuer à fonctionner.
Gilles 'SO- arrête d'être méchant'
Mon point est que vous ne pouvez pas simplement "désinfecter votre environnement" et ensuite cesser de vous soucier des différents vecteurs d'attaque. Parfois, il vaut mieux "se protéger contre un environnement hostile de l'intérieur du script". Même si vous résolvez le problème de "définition de fonction", vous devez toujours vous soucier de choses comme avoir un CHEMIN où quelqu'un pourrait mettre un script personnalisé nommé "cat" ou "ls" dans un répertoire, puis tout script qui invoque "cat "ou" ls "au lieu de" / bin / cat "et" / bin / ls "exécute efficacement un exploit.
Dejay Clayton
@DejayClayton De toute évidence, la désinfection de l'environnement n'aide pas avec les vecteurs d'attaque qui ne sont pas liés à l'environnement. (Par exemple, un script qui appelle /bin/catun chroot pourrait appeler n'importe quoi.) La désinfection de l'environnement vous donne un sens PATH(en supposant que vous le faites correctement bien sûr). Je ne vois toujours pas votre point.
Gilles 'SO- arrête d'être méchant'
Considérant que PATH est l'une des plus grandes opportunités d'exploits, et que de nombreuses pratiques PATH douteuses sont documentées comme des conseils recommandés même dans les blogs de sécurité, et considérant également que parfois les applications installées réorganisent PATH pour s'assurer que de telles applications fonctionnent, je ne vois pas comment un codage défensif dans un script serait une mauvaise idée. Ce que vous considérez comme un CHEMIN sain d'esprit peut en fait être vulnérable aux exploits que vous n'avez pas rencontrés.
Dejay Clayton
1

Vous pouvez utiliser unset -fpour supprimer les fonctions intégrées, la commande et le type.

odc
la source
2
désolé, unset() { true; }s'occupe de ça.
falstro
1
Zut! Et la liste des fonctions définies repose également sur une fonction intégrée. J'abandonne!
odc