Shellshock, automatizziamo il test

Qualche giorno fa abbiamo parlato di shellshock, dei danni che può fare e di come può propagarsi.

Photo by Karunakar Rayker

Qualche giorno fa abbiamo parlato di shellshock, dei danni che può fare e di come può propagarsi.

Vi ho rimandato, in un update del post, all’articolo originale che spiega due CVE, il CVE-2014-7186 e il CVE-2014-7187, che nel mio post avevo colpevolmente omesso.

Oggi parliamo invece di come testare la vulnerabilità quando abbiamo in gestione un gran numero di server unix.

Un passo oltre il vulnerability management

Potreste avere già in campo un processo di vulnerability management, quindi potreste avere un software di vulnerability assessment che periodicamente scansiona le vostre macchine per issue di security.

A seconda della schedulazione voi avrete quindi l’elenco delle macchine vulnerabili a shellshock con tutti i CVE che ne seguono.

Questo implica anche che il vostro tool abbia già tutte le firme per i possibili exploit legati a shellshock.

Il vostro capo però vuole una situazione aggiornata ora e non c’è tempo di aspettare che partano tutte le scansioni schedulate. Dobbiamo quindi arrangiarci.

Lista della spesa

Per automatizzare con successo il processo di verifica ci serve:

  • elenco completo dei server unix da testare
  • script che provi tutti i possibili exploit sulla bash
  • script per automatizzarne l’esecuzione

Sembra paradossale, ma forse l’elenco completo dei server unix da testare è la cosa più complessa da recuperare. Ci sono realtà dove agenti di software di asset inventory, provano a popolare per noi questo elenco. Ci sono realtà dove tutto è dentro un enorme foglio di calcolo. Ci sono realtà dove non c’è nulla e si lavora per subnet.

Qui dipende molto dalla vostra realtà. In dubbio, chiedete ai sysadmin. L’importante è che abbiate un elenco di indirizzi IP di macchine Unix, perché il nostro script non si metterà a fare un nmap per discovery e per riconoscere il sistema operativo.

Lo script per fare il check locale

Lo script per testare, localmente, tutte le varianti di shellshock lo potete trovare qui su github.com. Non fa altro che provare a mettere i diversi pattern d’attacco in una variabile d’ambiente e vedere come reagisce la shell.

Anche in questo caso però ho bisogno di fare qualche modifica. Lo script di hannob è bello, usa i colori, da messaggi completi, ma trovo l’output sia poco usabile in caso debba collezionare i dati di 1000 server.

Ho bisogno quindi di modificare lo script per ottenere in output un YES o NO divisi da virgole a seconda che l’exploit sia andato a buon fine o meno. Alla fine sto costruendo un enorme file CSV che potrò importare in un foglio di calcolo per fare un po’ di intelligence sui dati.

#!/bin/bash

[ -n "$1" ] && bash=$(which $1) || bash=$(which bash)
echo -n "$bash,"
echo -n "$($bash --version | head -n 1 | cut -f2 -d ","),"

#r=`a="() { echo x;}" $bash -c a 2>/dev/null`
if [ -n "$(env 'a'="() { echo x;}" $bash -c a 2>/dev/null)" ]; then
	echo -n "maybe vulnerable to unknown parser bugs,"
elif [ -n "$(env 'BASH_FUNC_a%%'="() { echo x;}" $bash -c a 2>/dev/null)" ]; then
	echo -n "bugs not exploitable,"
elif [ -n "$(env 'BASH_FUNC_a()'="() { echo x;}" $bash -c a 2>/dev/null)" ]; then
	echo -n "bugs not exploitable,"
elif [ -n "$(env 'BASH_FUNC_<a>%%'="() { echo x;}" $bash -c a 2>/dev/null)" ]; then
	echo -n "bugs not exploitable,"
else
	echo -n "bugs not exploitable,"
fi


r=`env x="() { :; }; echo x" $bash -c "" 2>/dev/null`
if [ -n "$r" ]; then
	echo -n "YES,"
else
	echo -n "NO,"
fi

cd /tmp;rm echo 2>/dev/null
env x='() { function a a>\' $bash -c echo 2>/dev/null > /dev/null
if [ -e echo ]; then
	echo -n "YES,"
else
	echo -n "NO,"
fi

$($bash -c "true $(printf '<<EOF %.0s' {1..80})" 2>/tmp/bashcheck.tmp)
ret=$?
grep AddressSanitizer /tmp/bashcheck.tmp
if [ $? == 0 ] || [ $ret == 139 ]; then
	echo -n "YES,"
else
	echo -n "NO,"
fi


$bash -c "`for i in {1..200}; do echo -n "for x$i in; do :;"; done; for i in {1..200}; do echo -n "done;";done`" 2>/dev/null
if [ $? != 0 ]; then
	echo -n "YES,"
else
	echo -n "NOT RELIABLE,"
fi

$($bash -c "f(){ x(){ _;};x(){ _;}<<a;}" 2>/dev/null)
if [ $? != 0 ]; then
	echo -n "YES,"
else
	echo -n "NO,"
fi

if [ -n "$(env x='() { _;}>_[$($())] { echo x;}' $bash -c : 2>/dev/null)" ]; then
	echo -n "YES,"
elif [ -n "$(env BASH_FUNC_x%%='() { _;}>_[$($())] { echo x;}' $bash -c : 2>/dev/null)" ]; then
	echo -n "YES,"
elif [ -n "$(env 'BASH_FUNC_x()'='() { _;}>_[$($())] { echo x;}' $bash -c : 2>/dev/null)" ]; then
	echo -n "YES,"
else
	echo -n "NO,"
fi

Provando questo script sul mio macbook, ottengo il seguente output:

/bin/bash, version 3.2.51(1)-release (x86_64-apple-darwin13),maybe vulnerable to unknown parser bugs,YES,YES,YES,NOT RELIABLE,YES,NO,

Colleziono quindi la shell che ho testato, la versione e poi l’output degli exploit. Lascio la virgola dopo l’ultimo risultato perché lo script che poi eseguirà i tentativi, appenderà l’hostname e se è riuscito ad collegarsi sulla macchina.

Lo script per automatizzare il tutto

Per eseguire questo script, vi servirà un utente locale sulle varie macchine che andrete a testare. Probabilmente ne avrete già uno, sostituite quindi la login corretta al valore ‘guest’.

Una volta lanciato lo script vi chiederà la password da utilizzare per le connessioni ssh, proverà a copiare lo script attaccante sulla macchina, lo eseguirà redirigendone l’output su un file, recupererà il file dalla macchina target e poi farà pulizia.

#!/bin/sh
APPNAME=`basename $0`
USER='guest'
EXPECT=`which expect`
BASHCKECK="$PWD/rawbashcheck"


###############################################################################
# shellshock finder script
# v1.0 - paolo@codiceinsicuro.it
###############################################################################

function help() {
  echo "$APPNAME - v1.0 - paolo@codiceinsicuro.it"
  echo "$APPNAME is a script to automate #shellshock check within a given list of hosts"
  echo "\nusage: $APPNAME hostlist.txt"
  echo "e.g. $APPNAME my_hostlist.txt"
}

function upload() {
  $EXPECT << EOD
  spawn scp -P $2 $BASHCKECK $USER@$1:~

  expect {
      -re ".*yes.*no.*" {
          exp_send "yes\r"
          exp_continue
      }
      -re ".*assword.*" {
          exp_send "$password\r"
          exp_continue
      }
      "Permission denied" {
        exit 5
      }
      "lost connection" {
        exit 5
      }
  }

EOD
}

function exploit() {
  $EXPECT << EOD
  spawn ssh -p $2 $USER@$1 ~/rawbashcheck > result.txt

  expect {
      -re ".*yes.*no.*" {
          exp_send "yes\r"
          exp_continue
      }
      -re ".*assword.*" {
          exp_send "$password\r"
          exp_continue
      }
  }

EOD
}

function download() {
  $EXPECT << EOD
  spawn scp -P $2 $USER@$1:~/result.txt .

  expect {
      -re ".*yes.*no.*" {
          exp_send "yes\r"
          exp_continue
      }
      -re ".*assword.*" {
          exp_send "$password\r"
          exp_continue
      }
  }

EOD
}

function clean_up() {
  $EXPECT << EOD
  spawn ssh -p $2 $USER@$1 rm ~/rawbashcheck ~/result.txt

  expect {
      -re ".*yes.*no.*" {
          exp_send "yes\r"
          exp_continue
      }
      -re ".*password.*" {
          exp_send "$password\r"
          exp_continue
      }
  }

EOD
}

###############################################################################
# So the story begins
###############################################################################

if ! [ -x $EXPECT ]; then
  echo "expect command not found"
  exit
fi

if ! [ -e $BASHCKECK ]; then 
  echo "$BASHCKECK not found"
  exit
fi

if [ $# -ne 1 ]; then
  help
  # exit
fi

echo "Please enter $USER password: "
read -s password


echo "BASH PATH, BASH VERSION, PARSER STATUS, CVE-2014-6271, CVE-2014-7169, CVE-2014-7186, CVE-2014-7187, CVE-2014-6277, CVE-2014-6278, HOST, REMOTE ACCESS" > out.txt

ips = `readarray -t array < $1`
for i in ${ips[@]}
do
  echo $i
done

exit

for i in ${ips[@]}
do
  echo "exploiting shellshock on $i"
  upload $i 10100
  if [ $? -ne 5 ]; then
    exploit $i 10100
    sleep 0.3
    download $i 10100
    sleep 0.3
    cat result.txt >> out.txt
    echo "$i, YES" >> out.txt
    clean_up $i 10100
    sleep 0.3
  else
    echo "- , - , - , - , - , - , - , - , -, $i, NO" >> out.txt
    sleep 0.6

  fi
done

L’output sarà quindi un file CSV contenente l’esito dei test sulla macchina. Ecco l’output dello script eseguito sul mio portatile.

BASH PATH, BASH VERSION, PARSER STATUS, CVE-2014-6271, CVE-2014-7169, CVE-2014-7186, CVE-2014-7187, CVE-2014-6277, CVE-2014-6278, HOST, REMOTE ACCESS
/bin/bash, version 3.2.51(1)-release (x86_64-apple-darwin13),maybe vulnerable to unknown parser bugs,YES,YES,YES,NOT RELIABLE,YES,NO, localhost, YES

Off by one

Se avete un migliaio di indirizzi IP da scansionare, eseguire questo script vi consentirà, non solo di verificare lo stato della macchina per la vulnerabilità di cui tutti parlano in questi giorni, ma anche di verificare che voi abbiate una login remota da utilizzare… ovviamente per security stuff.

Enjoy!

comments powered by Disqus