Ce script est un "mini-tripwire", disons que son fonctionnement n'y ressemble pas vraiment, mais l'intérêt s'en rapproche : créer une base de hash et vérifier que les binaires n'ont pas été altérés.
0x01. SCRIPT
#!/bin/sh # # Settings # ---------------------------------------------------------------------------------------------------- # APP="$(/usr/bin/basename "$0")" # Database only update by root, can be read by admin SYSDB="/usr/local/share/tripwire.db" # Log only written if run as root LOGDB="/var/log/$APP.log" [ ! -w "/var/log" ] && LOGDB="$PWD/$APP.log" # Common Unix folders BINPATHS="/bin /sbin /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin \ /usr/local/libexec /usr/libexec /usr/sfw/bin /usr/sfw/sbin \ /usr/sfw/libexec /opt/sfw/bin /opt/sfw/sbin /opt/sfw/libexec \ /usr/xpg4/bin /usr/css/bin /usr/ucb /usr/X11R6/bin /usr/X11R7/bin \ /usr/pkg/bin /usr/pkg/sbin /opt/local/bin /opt/local/sbin" # Colors W="\e[1;29m" # White R="\e[1;31m" # Red M="\e[1;30m" # Red G="\e[1;32m" # Green Y="\e[1;33m" # Yellow B="\e[0m" # Color, reset # Underline COLUMNS=100 # # Executable to protect file content # ---------------------------------------------------------------------------------------------------- # chflags="/usr/bin/chflags" # OS X chattr="/usr/bin/chattr" # RedHat-like (CentOS, Fedora, Redhat, ...) # # Restore crypted files if process is interrupted # ---------------------------------------------------------------------------------------------------- # trap exit SIGINT SIGTERM SIGSTOP SIGCONT # # _function are internal function. (used by functions) # Arg: filename # Ret: >1 if not executable [ -x "$exe" ] # and if its hash is not found in DB # TODO: if someone know how to grep the file "/bin/[" # ---------------------------------------------------------------------------------------------------- # _check() { # Base64 encoded binary exe="$1" bexe="$(echo "$exe"|base64)" # Absent from filesystem [ ! -f "$exe" ] && return 3 # Absent from DB ref="$(zgrep "$bexe;" "${SYSDB}.gz"|cut -d';' -f2|grep .)" >/dev/null 2>&1 || return 2 # Found in DB if [ "$(_getinfo "$exe"|cut -d';' -f2)" = "$ref" ]; then return 0 fi # Info changed return 1 } # # Protect the database for writting # ---------------------------------------------------------------------------------------------------- # _is_root() { ret=1 [ "$(/usr/bin/id -u)" = "0" ] && [ "$(/usr/bin/whoami)" = "root" ] && ret=0 return $ret } # # Protect the database for writting # ---------------------------------------------------------------------------------------------------- # _protect() { _is_root || return 0 file="$1" [ ! -f "$file" ] && return 1 /bin/chmod 444 "$file" if [ -x $chflags ]; then $chflags uchg "$file" $chflags schg "$file" fi } # # Unprotect the database for writting # ---------------------------------------------------------------------------------------------------- # _unprotect() { _is_root || return 0 file="$1" [ ! -f "$file" ] && return 1 if [ -x $chflags ]; then $chflags nouchg "$file" $chflags noschg "$file" fi /bin/chmod 600 "$file" } # # Log and display error # ---------------------------------------------------------------------------------------------------- # _error() { echo "[error];$0;$1" >> "${LOGDB}" echo "[x] Error: $1, see: '${LOGDB}'" [ $# -eq 2 ] && [ $2 -eq 1 ] && exit 1 } # # Underline # ---------------------------------------------------------------------------------------------------- # _title() { echo printf "$W" echo "$1"|tr "a-z" "A-Z" printf "$B" } # # Underline # ---------------------------------------------------------------------------------------------------- # _underline() { [ $# -eq 1 ] && COLUMNS=$1 for i in $(seq 1 $COLUMNS) ; do printf "—" ; done ; printf "\n" } # # Check a on demand file # ---------------------------------------------------------------------------------------------------- # checkexe() { exe="$1" verbose=0 ; [ $# -eq 2 ] && verbose=1 _check "$exe" ; err=$? if [ $err -eq 1 ]; then if [ $verbose -eq 1 ]; then ref="$(zgrep "$bexe" "${SYSDB}.gz"|cut -d';' -f2|base64 -d -i)" _title "File properties" _underline printf "$Y\nReference:$B" ; echo "$ref"|head -n1 printf "$R""Now .....:$B" ; /bin/ls -lO@ "$exe" printf "\n\n" _title "SHA 128 Hash" _underline printf "$Y\nReference:$B" ; echo "$ref"|head -n10|tail -n1 printf "$R""Now .....:$B" ; shasum "$exe" printf "\n\n" _title "Filetype" _underline printf "$Y\nReference:$B" ; echo "$ref"|head -n2|tail -n1 printf "$R""Now .....:$B" ; file "$exe" printf "\n\n" _title "48 first bytes + 48 last bytes" _underline printf "$Y\nReference:$B\n" ; echo "$ref"|head -n9|tail -n7 printf "$R\nNow .....:$B\n" ; ( hexdump -n48 -C "$exe" ; echo ' -- ' ; hexdump -C "$exe"|tail -n4 )|grep " .." printf "\n\n" echo else printf "\n[ $R""KO$B ] %-60s\n\n" "$exe" fi elif [ $err -eq 0 ]; then printf "\n[ $G""OK$B ] %-60s\n\n" "$exe" elif [ $err -eq 2 ]; then printf "\n[ $M""NO$B ] %-60s\n\n" "$exe" elif [ $err -eq 3 ]; then printf "\n[ $Y""!!$B ] %-60s\n\n" "$exe" fi return $err } # # Check hashes in database # ---------------------------------------------------------------------------------------------------- # checkdb() { echo i=0 printf "$G""OK$B: no changes, $R""KO$B: hash changed, $Y!!$B: read error\n\n" for folder in ${BINPATHS}; do for exe in "$folder"/* ; do [ ! -f "$exe" ] && continue if [ -x "$exe" ]; then _check "$exe" ; err=$? if [ $err -gt 0 -a $err -lt 3 ]; then printf "\n [%5s:$R""KO$B ] %-60s\n" "$i" "$exe" elif [ $err -eq 0 ]; then printf " [%5s:$G""OK$B ] %-60s\e[0K\r" "$i" "$exe" elif [ $err -eq 255 ]; then printf "\n [%5s:$Y""!!$B ] %-60s\n" "$i" "$exe" fi i=$((i+1)) fi done done printf "\n\n" } # # Get info about exe and encode in base64 (because of CR/LF) # ---------------------------------------------------------------------------------------------------- # _getinfo() { exe="$1" [ ! -f "$exe" ] && return 1 bexe="$(echo "$exe"|base64 2>/dev/null)" printf "$bexe;" ( /bin/ls -lO@ $exe file $exe ( hexdump -n48 -C "$exe" ; echo ' -- ' ; hexdump -C "$exe"|tail -n4 ) |grep " .." shasum $exe )|base64|tr -d "\n" echo } # # Update the database # ---------------------------------------------------------------------------------------------------- # updatedb() { renice -20 $$ tmp="$(mktemp)" /bin/chmod 600 "$tmp" printf "\n[%6s] Updating hash database : %-60s" " " "$exe" _unprotect "${SYSDB}" _unprotect "${SYSDB}.gz" /bin/rm -f "${SYSDB}.gz" "${SYSDB}" echo "${SYSOK}" > "${SYSDB}" i=0 for folder in ${BINPATHS}; do [ ! -d "$folder" ] && continue for exe in "$folder"/* ; do [ -d "$exe" ] && continue _getinfo "$exe" >> "${SYSDB}" || echo "[sysdb] [x] $exe" printf "[%6s] Updating hash database : %-60s\e[0K\r" "$i" "$exe" i=$((i+1)) done done /usr/bin/sort -u "${SYSDB}" > "$tmp" /bin/mv -f "$tmp" "${SYSDB}" /usr/bin/gzip "${SYSDB}" _protect "${SYSDB}.gz" printf "\r[%6s] Updating hash database : %-60s\n\n" "Done " "$exe" } # # Test user ID and username to known if we are root # ---------------------------------------------------------------------------------------------------- # is_root() { ret=1 [ "$(id -u)" = "0" ] && [ "$(whoami)" = "root" ] && ret=0 return $ret } # # Show synoptic # ---------------------------------------------------------------------------------------------------- # usage() { printf " Usage: $(basename $0) <-u|-C|-c file> -u : update hashes in database (only root) -C : check all binaries database -c file : check file Results: $G OK$B : file integrity is correct $R KO$B : file integrity changed $M NO$B : file is absent from database $Y !!$B : file is absent from filesystem " exit 0 } # # Argument parsing # ---------------------------------------------------------------------------------------------------- # if [ $# -lt 1 -o $# -gt 2 ]; then usage fi # Create log and database folders if root if is_root; then /bin/mkdir -p "$(/usr/bin/dirname "$SYSDB")" /bin/mkdir -p "$(/usr/bin/dirname "$LOGDB")" else # If there is a database, copy it to a temporary # location to be enable to make checks if [ ! -f "${SYSDB}.gz" ]; then _error "contact administrator to create database." 1 fi fi while getopts "uCc:h" option; do case $option in "u") is_root || exit 1 ; updatedb ;; "C") checkdb ;; "c") checkexe "$OPTARG" 1; exit $? ;; "h") usage ;; *) usage ;; esac done
=> Écrit par : Nicolas, le 01 mars 2016