|
Server IP : 217.21.85.138 / Your IP : 216.73.216.103 Web Server : LiteSpeed System : Linux in-mum-web906.main-hosting.eu 4.18.0-553.37.1.lve.el8.x86_64 #1 SMP Mon Feb 10 22:45:17 UTC 2025 x86_64 User : u915722082 ( 915722082) PHP Version : 7.4.33 Disable Function : system, exec, shell_exec, passthru, mysql_list_dbs, ini_alter, dl, symlink, link, chgrp, leak, popen, apache_child_terminate, virtual, mb_send_mail MySQL : OFF | cURL : ON | WGET : ON | Perl : OFF | Python : OFF Directory (0755) : /home/u915722082/public_html/ |
| [ Home ] | [ C0mmand ] | [ Upload File ] |
|---|
<?php
/**
* scan-suspicious.php
* Place: /home/u915722082/domains/thedotstudios.com/public_html/scan-suspicious.php
*
* Scans /home/u915722082/domains for suspicious files and writes:
* - suspicious-report.txt (detailed) next to this script
* - appends a summary to attack-report.txt and cron-log.txt (next to this script)
*
* Run: php scan-suspicious.php
*/
date_default_timezone_set('Asia/Kolkata');
$SCRIPT_DIR = __DIR__; // /home/.../thedotstudios.com/public_html
$BASE = '/home/u915722082/domains';
$REPORT = $SCRIPT_DIR . '/suspicious-report.txt';
$ATTACK_REPORT = $SCRIPT_DIR . '/attack-report.txt';
$CRON_LOG = $SCRIPT_DIR . '/cron-log.txt';
$SNAPSHOT = $SCRIPT_DIR . '/filelist.json';
/* ---------- Config ---------- */
$RECENT_DAYS = 7; // consider files modified within last N days
$MAX_READ_BYTES = 131072; // read at most 128KB of each file
$CHECK_PHP_CONTENT = true;
$USE_SNAPSHOT = true;
// Ensure report files exist and are writable
foreach ([$REPORT, $ATTACK_REPORT, $CRON_LOG] as $f) {
if (!file_exists($f)) {
@file_put_contents($f, "Created: " . date('Y-m-d H:i:s') . PHP_EOL);
@chmod($f, 0644);
}
}
// Exclusions to reduce noise (adjust if needed)
$EXCLUDES = [
__FILE__,
$REPORT,
$ATTACK_REPORT,
$CRON_LOG,
$SNAPSHOT,
$SCRIPT_DIR . '/logs', // central logs dir (don't scan logs folder)
'/home/u915722082/domains/*/public_html/storage/framework/sessions',
'/home/u915722082/domains/*/public_html/cache',
];
/* ---------- Suspicious rules ---------- */
$suspiciousNames = [
'/\bzz\.php\b/i', '/\bshell\.php\b/i', '/\bbackdoor\b/i', '/\bcmd\.php\b/i',
'/\bphpinfo\.php\b/i', '/\bx\.php\b/i', '/\buploader\.php\b/i', '/\bupload\.php\b/i'
];
$suspiciousContent = [
'/eval\s*\(/i' => 'uses eval()',
'/base64_decode\s*\(/i' => 'uses base64_decode()',
'/gzinflate\s*\(/i' => 'uses gzinflate()/gzuncompress()',
'/gzuncompress\s*\(/i' => 'uses gzuncompress()',
'/str_rot13\s*\(/i' => 'uses str_rot13()',
'/shell_exec\s*\(/i' => 'uses shell_exec()',
'/system\s*\(/i' => 'uses system()',
'/passthru\s*\(/i' => 'uses passthru()',
'/exec\s*\(/i' => 'uses exec()',
'/preg_replace\s*\(\s*.*\/e.*/i' => 'uses preg_replace with /e modifier',
'/assert\s*\(/i' => 'uses assert()',
'/curl_exec\s*\(/i' => 'uses curl_exec()',
'/fopen\s*\(\s*[\'"]http/i' => 'opens remote http stream',
'/[A-Za-z0-9\/+]{200,}/' => 'very long base64-like string (possible encoded payload)'
];
$suspiciousPaths = [
'/\/uploads\//i',
'/wp-content\/uploads/i',
'/\/tmp\//i',
'/\/cache\//i',
'/\/backup\//i',
];
/* ---------- Helpers ---------- */
function is_world_writable($perm_octal) {
$perm = intval($perm_octal, 8);
// check group or other write bit
return (bool) (($perm & 0x0002) || ($perm & 0x0010) || ($perm & 0x0020));
}
function match_excludes($path, $excludes) {
foreach ($excludes as $ex) {
if (strpos($ex, '*') !== false) {
$pattern = '#^' . str_replace('\\*', '.*', preg_quote($ex, '#')) . '$#i';
if (preg_match($pattern, $path)) return true;
} else {
if (strpos($path, $ex) === 0) return true;
}
}
return false;
}
/* ---------- Load snapshot if any ---------- */
$prev = [];
if ($USE_SNAPSHOT && file_exists($SNAPSHOT)) {
$j = @file_get_contents($SNAPSHOT);
$prev = json_decode($j, true) ?: [];
}
/* ---------- Walk files ---------- */
$rii = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($BASE, FilesystemIterator::SKIP_DOTS));
$suspicious = [];
$now = time();
$recent_threshold = $now - ($RECENT_DAYS * 86400);
foreach ($rii as $file) {
if (!$file->isFile()) continue;
$path = $file->getPathname();
if (match_excludes($path, $EXCLUDES)) continue;
$ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));
$size = $file->getSize();
$mtime = $file->getMTime();
$owner = function_exists('posix_getpwuid') ? (@posix_getpwuid(@fileowner($path))['name'] ?? @fileowner($path)) : @fileowner($path);
$perm = substr(sprintf('%o', @fileperms($path)), -4);
$reasons = [];
// filename patterns
foreach ($suspiciousNames as $pat) {
if (preg_match($pat, $path)) { $reasons[] = "Suspicious filename pattern matched: {$pat}"; break; }
}
// suspicious path patterns
foreach ($suspiciousPaths as $pat) {
if (preg_match($pat, $path)) {
if ($ext === 'php') $reasons[] = "PHP in suspicious path: {$pat}";
else $reasons[] = "File in suspicious path: {$pat}";
break;
}
}
// owner is typically webserver user?
$lowowner = strtolower((string)$owner);
if (in_array($lowowner, ['nobody','www-data','apache','httpd','wwwrun'])) {
$reasons[] = "Owned by webserver user: {$owner}";
}
// world/group writable
if (is_world_writable($perm)) {
$reasons[] = "Group/other writable (perm {$perm})";
}
// recent file
if ($mtime >= $recent_threshold) {
$reasons[] = "Recently modified: " . date('Y-m-d H:i:s', $mtime);
}
// new since snapshot
if ($USE_SNAPSHOT && !isset($prev[$path])) {
$reasons[] = "New file since last snapshot";
}
$excerpt = '';
if ($ext === 'php' && $CHECK_PHP_CONTENT) {
$content = @file_get_contents($path, false, null, 0, $MAX_READ_BYTES);
if ($content !== false && $content !== '') {
foreach ($suspiciousContent as $pat => $desc) {
if (preg_match($pat, $content)) $reasons[] = $desc . " (pattern: $pat)";
}
if (preg_match('/[A-Za-z0-9\/+]{200,}/', $content)) $reasons[] = 'Very long base64-like string detected';
if (preg_match_all('/chr\s*\(/i', $content) > 5) $reasons[] = 'Many chr() calls (possible obfuscation)';
// excerpt
$lines = preg_split("/\r\n|\n|\r/", $content);
$excerpt = implode("\n", array_slice($lines, 0, 12));
} else {
$excerpt = '[unreadable or empty]';
}
}
if (!empty($reasons)) {
$suspicious[$path] = [
'owner' => $owner,
'perm' => $perm,
'size' => $size,
'mtime' => date('Y-m-d H:i:s', $mtime),
'reasons' => $reasons,
'excerpt' => $excerpt
];
}
}
/* ---------- Write report ---------- */
$nowLabel = date('Y-m-d H:i:s');
$header = "Suspicious scan run at: $nowLabel\nBase: $BASE\nDetected: ".count($suspicious)." suspicious files\n\n";
file_put_contents($REPORT, $header, LOCK_EX);
foreach ($suspicious as $path => $info) {
$block = "-------------------------------\n";
$block .= "File: $path\n";
$block .= "Owner: {$info['owner']} Perm: {$info['perm']} Size: {$info['size']} MTime: {$info['mtime']}\n";
$block .= "Reasons:\n";
foreach ($info['reasons'] as $r) $block .= " - $r\n";
if (!empty($info['excerpt'])) {
$block .= "Excerpt (first lines / <=128KB):\n";
$block .= $info['excerpt'] . "\n";
}
$block .= "\n";
file_put_contents($REPORT, $block, FILE_APPEND | LOCK_EX);
}
// append summary to attack-report and cron-log
$appendBlock = "=== Suspicious scan summary at $nowLabel ===\nDetected: ".count($suspicious)." suspicious files. See suspicious-report.txt\n\n";
file_put_contents($ATTACK_REPORT, $appendBlock, FILE_APPEND | LOCK_EX);
file_put_contents($CRON_LOG, $appendBlock, FILE_APPEND | LOCK_EX);
/* ---------- Update snapshot ---------- */
if ($USE_SNAPSHOT) {
$snap = [];
$rii2 = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($BASE, FilesystemIterator::SKIP_DOTS));
foreach ($rii2 as $file) {
if ($file->isFile()) {
$p = $file->getPathname();
if (match_excludes($p, $EXCLUDES)) continue;
$snap[$p] = ['size' => $file->getSize(), 'mtime' => $file->getMTime()];
}
}
file_put_contents($SNAPSHOT, json_encode($snap, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES), LOCK_EX);
}
echo "Scan complete. Found ".count($suspicious)." suspicious files. Report: $REPORT\n";
?>