Il est particulièrement courant qu’au sein d’une entreprise on retrouve des scripts shell (Bash/sh) sur des environnements Linux en production. Ces scripts, développés « maison », via des auditeurs ou encore des sociétés spécialisées telles que SYNETIS, sont généralement indispensables pour le bon fonctionnement d’une infrastructure.

Ces scripts peuvent être aussi divers que variés. A titre d’exemple, de tels scripts sont souvent :

  • Des scripts de sauvegardes/backups d’annuaires ou de base de données, exécutés périodiquement (tâches cron)
  • Des scripts de nettoyage de caches/fichiers, d’archivage ou encore de compression
  • Des systèmes de surveillances/monitoring qui vérifient l’utilisation RAM/CPU/DISK d’un serveur
  • Des moyens d’externaliser des backups sur des emplacements réseaux distants

Tous les administrateurs systèmes ont déjà été amenés au moins une fois à exploiter de tels scripts.

Leur inconvénient principal est qu’ils contiennent régulièrement des informations critiques au sein de leur code source visible en clair avec les permissions adéquates. En effet, pour un script de sauvegarde d’une base de données, il est nécessaire de fournir le mot de passe principal à même le script. Idem pour un annuaire, ou encore un envoi de données sur un espace réseau protégé. Hélas, ces informations sensibles sont stockées en clair au sein de ces scripts.

Certaines bonnes pratiques permettent toutefois d’accroître la sécurité de ces fichier sensibles sous un environnement Linux :

  • Forcer la demande de mot de passe/passephrase à un utilisateur dédié lors de l’exécution du script. Certes cette méthode nécessite une intervention manuelle mais reste une des plus sûres.
  • Pour les scripts en tâches panifiées, ceux-ci doivent être autonomes et ne doivent pas nécessiter d’intervention manuelle. Ainsi, ces scripts sont généralement confinés dans des répertoires du système protégés (/root) afin d’être uniquement exécutables/modifiables par un utilisateurs aux permissions suffisantes.
  • Les mots de passes contenus dans ces fichiers sont très souvent changés et complexifiés lors d’une mise en production.
  • Enfin, il est possible de chiffrer ces scripts afin d’amoindrir les chances d’en visualiser le contenu (et les mots de passes) en clairs.

Cet article est dédié à ce dernier point, « le chiffrement de script Bash/Sh ».

Une solution existe depuis plusieurs années, permettant de transformer un script « *.sh » en un binaire dont le code source du script initial est entièrement chiffré et embarqué dans ce binaire. L’idée est d’empêcher la visualisation du code source du script en clair et de décourager certains curieux aux actions malveillantes (bien que les plus téméraires en viendront à bout).

La solution se nomme « shc » pour « SHell Compiler ». Ce petit outil OpenSource développé par Francisco Javier Rosales Garcia dont la dernière version en date est la 3.8.9 permet de chiffrer n’importe quel script interprété sous un terminal Linux.

Il suffit d’équiper une machine Linux de l’outil pour chiffrer les scripts de son choix, avant de mettre les versions chiffrées en production :

wget -q http://www.datsi.fi.upm.es/~frosal/sources/shc-3.8.9.tgz
tar zxvf shc-3.8.9.tgz
cd shc-3.8.9
ln -s shc-3.8.9.c shc.c
make

Après la compilation de l’outil, un binaire du nom de « shc » est présent dans le répertoire.

L’outil s’utilise très simplement :

root@server:~/shc/shc-3.8.9# shc -h
shc Version 3.8.9, Generic Script Compiler
shc Copyright (c) 1994-2012 Francisco Rosales <frosal@fi.upm.es>
shc Usage: shc [-e date] [-m addr] [-i iopt] [-x cmnd] [-l lopt] [-rvDTCAh] -f script
-e %s Expiration date in dd/mm/yyyy format [none]
 -m %s Message to display upon expiration ["Please contact your provider"]
 -f %s File name of the script to compile
 -i %s Inline option for the shell interpreter i.e: -e
 -x %s eXec command, as a printf format i.e: exec('%s',@ARGV);
 -l %s Last shell option i.e: --
 -r Relax security. Make a redistributable binary
 -v Verbose compilation
 -D Switch ON debug exec calls [OFF]
 -T Allow binary to be traceable [no]
 -C Display license and exit
 -A Display abstract and exit
 -h Display help and exit
Environment variables used:
 Name Default Usage
 CC cc C compiler command
 CFLAGS  C compiler flags
Please consult the shc(1) man page.

Pour illustrer son utilisation, voici un script d’exemple qui doit être déployé sur un environnement de production :

#!/bin/bash
# @Author : ycam - synetis
DATE=`date +"%Y%m%d%H%M"`
BACKUPFOLDER="/var/lib/ldap/backup"
LDAPSEARCHBIN="ldapsearch"
AUTHDN="cn=root,dc=domain,dc=local"
AUTHPW="MySuperStrongPassWordOfProductionEnv"
SERVER="ldap://localhost"
BRANCH="dc=domain,dc=local"
mkdir -p $BACKUPFOLDER
$LDAPSEARCHBIN -x -D "$AUTHDN" -w $AUTHPW -b "$BRANCH" -H $SERVER -LLL > $BACKUPFOLDER/$DATE-backup.ldif
tar zcf $BACKUPFOLDER/$DATE-backup.tgz -C $BACKUPFOLDER/ $BACKUPFOLDER/$DATE-backup.ldif
rm -f $BACKUPFOLDER/$DATE-backup.ldif
echo "Backup $BACKUPFOLDER/$DATE-backup.ldif done !"

Il est aisé de remarquer que ce script Bash de sauvegarde d’un annuaire au format LDIF, nécessite les crédentiels de l’accès « rootdn » au LDAP. L’information « AUTHPW » est critique. Protégeons ce script via shc :

root@server:~/shc/shc-3.8.9# shc -v -r -f backup_ldap.sh
shc shll=bash
shc [-i]=-c
shc [-x]=exec '%s' "$@"
shc [-l]=
shc opts=
shc: cc backup_ldap.sh.x.c -o backup_ldap.sh.x
shc: strip backup_ldap.sh.x
shc: chmod go-r backup_ldap.sh.x
root@server:~/shc/shc-3.8.9# ll backup_ldap.sh*
-rw-r--r-- 1 root root 525 janv. 12 23:54 backup_ldap.sh
-rwx--x--x 1 root root 10944 janv. 12 23:54 backup_ldap.sh.x*
-rw-r--r-- 1 root root 12045 janv. 12 23:54 backup_ldap.sh.x.c

Après l’exécution de « shc » avec ses options (-v pour la verbosité, -f pour le script d’entrée et -r pour rendre le binaire compatible avec d’autres distributions/architectures), deux fichiers sont produits : un fichier « .x » et un « .x.c ». Le premier est le binaire chiffré qui est amené à remplacer le .sh originel ; le second est le code source généré à la volée par « shc » pour notre script à chiffrer.

Chaque exécution de shc sur un même script donnera un « .x.c » différent, avec une clé différente et une structuration du code différente.

L’exécution du script chiffré est en tout point similaire au script original :

root@server:~/shc/shc-3.8.9# ./backup_ldap.sh
Backup /var/lib/ldap/backup/201301130003-backup.ldif done !
root@server:~/shc/shc-3.8.9# ./backup_ldap.sh.x
Backup /var/lib/ldap/backup/201301130003-backup.ldif done !

Celui-ci peut être placé sur un serveur de production afin d’augmenter la sécurité de celui-ci. Bien évidemment, il est conseillé de conserver le script original dont la source est accessible.

Le fonctionnement général de shc est le suivant :

  • Analyse du script en entrée
  • Chiffrement de son code source à l’aide d’une clé RC4 unique
  • Génération d’un code source C dont la structure est aléatoire
  • Ce code source C contient la version chiffrée du code source du script Bash/Sh initial, ainsi que les vecteurs clés de déchiffrement.
  • Ce code source C contient également l’algorithme inverse de déchiffrement
  • Ce code source « *.x.c » est compilé pour donner un binaire final.
  • A l’exécution de ce binaire, les variables chiffrées qu’il renferme sont déchiffrées à la volée avec la clé qu’il contient également. Puis la source déchiffrée est interprétée directement via « execvp() » pour produire le résultat attendu.

L’outil shc permet également d’intégrer une notion de validité temporelle du script. Au delà d’une certaine date (temps d’utilisation), son exécution est bloquée pour laisser place à un message arbitraire.

Shc est un outil dont tous administrateurs systèmes et intégrateurs devraient se munir pour renforcer la sécurité des scripts critiques en production. Il est l’un des rares à assurer une protection relativement efficace sur les scripts Bash/Sh. Toutefois, comme détaillé préalablement, le fichier binaire résultant embarque le code source initial du script (chiffré), ainsi que tout le « matériel cryptographique » (clé, vecteur…) nécessaire à son auto-déchiffrement. Il est tout à fait possible d’extraire d’un binaire chiffré « *.x » le code source original « *.sh ». Un PoC sera bientôt présenté afin d’illustrer ces propos.

Outre le fait que cette solution de qualité ne soit pas infaillible, SYNETIS vous conseille d’exploiter le plus de techniques possibles afin de sécuriser au mieux vos scripts en environnements de production.

Sources & ressources :

Yann CAM

Consultant Sécurité