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.







 
#!/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


 
Mots clés :  
  security 
  
  harden 
    >   Articles connexes :

/tmp et /var/log en noexec sur macOS

/tmp et /var/log en noexec sur macOS



Durcissement de Windows

Durcissement de Windows



OpenSCAP

OpenSCAP


2177484