Edit File: post_imunify_detection.php
#!/usr/bin/php -q <?php date_default_timezone_set('Asia/Kolkata'); $timestamp = date("Y-m-d H:i:s"); // —————————————————————————————————————————————— // 1) Bootstrap & logger // —————————————————————————————————————————————— while (ob_get_level()) { ob_end_clean(); } function logit(string $msg): void { // (re)set your timezone if needed date_default_timezone_set('Asia/Kolkata'); // build the timestamp $timestamp = date('Y-m-d H:i:s'); // append to the log file_put_contents( '/usr/local/cptools/logs/abuseadmin_activity.log', "[$timestamp] $msg\n", FILE_APPEND | LOCK_EX ); } // —————————————————————————————————————————————— // 2) Config & CLI args // —————————————————————————————————————————————— $dataFile = '/usr/local/cptools/AbuseAdmin/post_imunify_action_data'; $dataDir = dirname($dataFile); $dataFile1 = '/usr/local/cptools/AbuseAdmin/TicketUsers'; $dataDir1 = dirname($dataFile1); // ensure directory exists if (! is_dir($dataDir)) { mkdir($dataDir, 0755, true); } // ensure data file exists and is writable by hook user if (! file_exists($dataFile)) { touch($dataFile); } // set permissive mode so Imunify/WHM user can append chmod($dataFile, 0666); // <<< NEW: ensure TicketUsers exists >>> if (! file_exists($dataFile1)) { // if the directory itself might not exist, create it first: if (! is_dir($dataDir1)) { mkdir($dataDir1, 0755, true); } touch($dataFile1); } chmod($dataFile1, 0666); // parse key=val CLI args $params = []; for ($i = 1; $i < $argc; $i++) { if (strpos($argv[$i], '=') !== false) { list($k, $v) = explode('=', $argv[$i], 2); $params[$k] = $v; } } // —————————————————————————————————————————————— // 3) Purge entries ≥ 24h old // —————————————————————————————————————————————— $lines = file($dataFile1, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); $clean = []; $seenUser = []; // for duplicate-check later $nowTs = time(); $cutoff = 24 * 3600; foreach ($lines as $line) { if (preg_match('/^\[(?<ts>[\d\- :]+)\]\s+(?<user>\S+)$/', $line, $m)) { $entryTs = strtotime($m['ts']); if (($nowTs - $entryTs) < $cutoff) { $clean[] = $line; $seenUser[$m['user']] = true; } else { //logit("Purged expired entry: {$line}"); } } else { // malformed line: keep it $clean[] = $line; } } // overwrite with only fresh entries file_put_contents($dataFile1, implode(PHP_EOL, $clean) . PHP_EOL, LOCK_EX); // —————————————————————————————————————————————— // 4) remove-mode // —————————————————————————————————————————————— if (isset($params['remove'], $params['username']) && $params['remove'] === '1') { $user = $params['username']; logit("Remove mode: removing '{$user}'"); if (! is_readable($dataFile) || ! is_writable($dataFile)) { //logit("ERROR: cannot read/write {$dataFile}"); exit(1); } $lines = file($dataFile, FILE_IGNORE_NEW_LINES); $out = []; foreach ($lines as $line) { if (strcasecmp(trim($line), $user) !== 0) { $out[] = $line; } } if (false === file_put_contents($dataFile, implode("\n", $out) . "\n", LOCK_EX)) { //logit("ERROR: failed to write {$dataFile}"); exit(1); } //logit("Successfully removed '{$user}'"); exit(0); } // —————————————————————————————————————————————— // 4) Read event JSON from STDIN // —————————————————————————————————————————————— $stdin = fopen('php://stdin','r'); $payload = ''; while (! feof($stdin)) { $payload .= fgets($stdin); } fclose($stdin); $json = json_decode(trim($payload), true); if (! is_array($json) || $json['event'] !== 'malware-detected') { //logit("Skipping: not a malware-detected event"); exit(0); } // ensure report file if (empty($json['params']['tmp_filename']) || ! is_readable($json['params']['tmp_filename'])) { //logit("ERROR: Missing or unreadable tmp_filename"); exit(1); } $reportFile = $json['params']['tmp_filename']; // —————————————————————————————————————————————— // 5) Load & parse report // —————————————————————————————————————————————— $report = json_decode(file_get_contents($reportFile), true); if (! is_array($report)) { //logit("ERROR: invalid JSON in {$reportFile}"); exit(1); } // —————————————————————————————————————————————— // 6) Process each infected entry // —————————————————————————————————————————————— $handled = []; $already = array_map('trim', file($dataFile)); foreach ($report as $entry) { if (empty($entry['username'])) { continue; } $user = $entry['username']; // determine file without ?? operator if (isset($entry['file']) && $entry['file'] !== '') { $file = $entry['file']; } else { $file = 'unknown'; } // —————————————————————————————————————————————— // Only block files under /home, /home2, /home3, etc. // —————————————————————————————————————————————— if (! preg_match('#^/home[0-9]*/#', $file) ) { logit("File '{$file}' unable to block: outside of /home*/ directories"); // stop processing (exit gracefully) exit(0); } // skip duplicates or already handled if (in_array($user, $handled, true) || in_array($user, $already, true)) { logit("'{$user}': already blocked\n\n "); continue; } $handled[] = $user; // determine domain $mainConf = "/var/cpanel/userdata/{$user}/main"; $domain = 'unknown'; if (is_readable($mainConf)) { $cfg = file_get_contents($mainConf); if (preg_match('/^main_domain:\s*(\S+)$/m', $cfg, $m)) { $domain = $m[1]; } } logit("AbuseAdmin triggered for $domain with cPanel user $user"); // run your abuse block $cmd = sprintf('/usr/local/cptools/cptools_abuse.cgi --block --domain=%s --user=%s --file=%s',escapeshellarg($domain),escapeshellarg($user),escapeshellarg($file)); logit("Command : $cmd"); exec($cmd . ' 2>&1', $output, $exitCode); /*logit("EXIT CODE: $exitCode"); foreach ($output as $i => $line) { logit("OUTPUT[$i]: $line"); }*/ // on success, append user if (false === file_put_contents($dataFile, $user . "\n", FILE_APPEND | LOCK_EX)) { logit("Could not append '{$user}'"); } else { logit("Finished processing malware-detected hook\n\n"); } } exit(0); ?>