Last active
October 18, 2025 23:42
-
-
Save uzielweb/74b81766d2d2eaf67fb531431587e5a0 to your computer and use it in GitHub Desktop.
Joomla Security Scanner - Ferramenta completa para detectar malware e verificar saúde dos arquivos do Joomla
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /** | |
| * Joomla Security Scanner | |
| * Verifica a saúde dos arquivos do Joomla e detecta possíveis malwares | |
| * | |
| * DESCRIÇÃO: | |
| * Este scanner realiza uma análise completa de segurança em instalações Joomla, | |
| * detectando potenciais ameaças, vulnerabilidades e problemas de configuração. | |
| * | |
| * FUNCIONALIDADES: | |
| * - Detecta mais de 40 assinaturas de malware conhecidas (shells, backdoors, webshells) | |
| * - Identifica funções PHP perigosas (eval, base64_decode, system, exec, etc.) | |
| * - Analisa código ofuscado e suspeito | |
| * - Verifica permissões de arquivos críticos | |
| * - Valida configurações de segurança (debug mode, error reporting) | |
| * - Examina arquivos .htaccess para regras maliciosas | |
| * - Detecta nomes de arquivos suspeitos (MD5 hashes, nomes curtos) | |
| * - Verifica diretórios temporários e cache | |
| * - Gera relatório detalhado em tempo real e em arquivo TXT | |
| * | |
| * COMO USAR: | |
| * 1. Via CLI (recomendado): php joomla-security-scanner.php | |
| * 2. Via navegador: acesse http://seusite.com/joomla-security-scanner.php | |
| * (Configure senha nas linhas 477-502 para segurança) | |
| * | |
| * IMPORTANTE: | |
| * - DELETE este arquivo após o uso por questões de segurança | |
| * - Faça backup antes de remover arquivos suspeitos | |
| * - Investigue manualmente cada ameaça detectada | |
| * - Mantenha Joomla e extensões sempre atualizados | |
| * | |
| * @version 1.0 | |
| * @author Uziel Web (uzielweb) | |
| * @date 2025-10-18 | |
| * @license MIT | |
| * @link https://gist.github.com/uzielweb/74b81766d2d2eaf67fb531431587e5a0 | |
| */ | |
| // Configurações | |
| error_reporting(E_ALL); | |
| ini_set('display_errors', 1); | |
| set_time_limit(0); | |
| ini_set('memory_limit', '512M'); | |
| // Caminho raiz do Joomla (ajuste conforme necessário) | |
| define('JOOMLA_ROOT', dirname(__FILE__)); | |
| class JoomlaSecurityScanner { | |
| private $results = []; | |
| private $suspiciousFiles = []; | |
| private $modifiedFiles = []; | |
| private $permissionIssues = []; | |
| private $malwareSignatures = []; | |
| private $scannedFiles = 0; | |
| private $startTime; | |
| public function __construct() { | |
| $this->startTime = microtime(true); | |
| $this->initMalwareSignatures(); | |
| } | |
| /** | |
| * Inicializa assinaturas de malware conhecidas | |
| */ | |
| private function initMalwareSignatures() { | |
| $this->malwareSignatures = [ | |
| // Funções perigosas | |
| 'eval\s*\(', | |
| 'base64_decode\s*\(', | |
| 'gzinflate\s*\(', | |
| 'gzuncompress\s*\(', | |
| 'str_rot13\s*\(', | |
| 'assert\s*\(', | |
| 'system\s*\(', | |
| 'exec\s*\(', | |
| 'shell_exec\s*\(', | |
| 'passthru\s*\(', | |
| 'proc_open\s*\(', | |
| 'popen\s*\(', | |
| 'curl_exec\s*\(', | |
| 'curl_multi_exec\s*\(', | |
| 'parse_ini_file\s*\(', | |
| 'show_source\s*\(', | |
| // Padrões suspeitos | |
| '\$_GET.*eval', | |
| '\$_POST.*eval', | |
| '\$_REQUEST.*eval', | |
| '\$_COOKIE.*eval', | |
| 'move_uploaded_file', | |
| 'file_put_contents.*\$_(GET|POST|REQUEST)', | |
| 'fwrite.*\$_(GET|POST|REQUEST)', | |
| 'fputs.*\$_(GET|POST|REQUEST)', | |
| // Backdoors conhecidos | |
| 'c99shell', | |
| 'r57shell', | |
| 'WSO\s*shell', | |
| 'FilesMan', | |
| 'Mysql\s*interface', | |
| 'phpspy', | |
| 'angel[\s_]?shell', | |
| 'haxor', | |
| 'h4x0r', | |
| 'c0derz', | |
| 'w4ck1ng', | |
| // Codificação suspeita | |
| '(?:[A-Za-z0-9+/]{4}){50,}={0,2}', // Base64 longo | |
| 'chr\s*\(\s*\d+\s*\)\s*\.', | |
| '\\\x[0-9a-fA-F]{2}', | |
| // Backdoor específico | |
| 'preg_replace.*\/e', | |
| 'create_function', | |
| '\$\{["\']?_[A-Z]+\}', | |
| 'ob_get_contents', | |
| // Funções de rede suspeitas | |
| 'fsockopen\s*\(', | |
| 'socket_create\s*\(', | |
| 'socket_connect\s*\(', | |
| // Ofuscação | |
| '@\$_\\\[\\\]', | |
| '\$\{"\w+"\}', | |
| '\$\{\$\w+\}', | |
| // Webshells | |
| 'uploadfile', | |
| 'cmd\s*=', | |
| 'uname\s*-a', | |
| 'whoami', | |
| '\/etc\/passwd', | |
| ]; | |
| } | |
| /** | |
| * Executa o scan completo | |
| */ | |
| public function scan() { | |
| $this->log("=== JOOMLA SECURITY SCANNER ===", 'info'); | |
| $this->log("Iniciando scan em: " . JOOMLA_ROOT, 'info'); | |
| $this->log("Data/Hora: " . date('Y-m-d H:i:s'), 'info'); | |
| $this->log("", 'info'); | |
| // Verifica se é um diretório válido do Joomla | |
| if (!$this->isJoomlaDirectory()) { | |
| $this->log("ERRO: Diretório não parece ser uma instalação válida do Joomla!", 'error'); | |
| return false; | |
| } | |
| // Scan de arquivos | |
| $this->log("1. Iniciando scan de arquivos...", 'info'); | |
| $this->scanDirectory(JOOMLA_ROOT); | |
| // Verifica permissões | |
| $this->log("2. Verificando permissões de arquivos críticos...", 'info'); | |
| $this->checkPermissions(); | |
| // Verifica configuration.php | |
| $this->log("3. Analisando configuration.php...", 'info'); | |
| $this->checkConfiguration(); | |
| // Verifica arquivos .htaccess | |
| $this->log("4. Verificando arquivos .htaccess...", 'info'); | |
| $this->checkHtaccess(); | |
| // Verifica diretórios temporários | |
| $this->log("5. Verificando diretórios temporários...", 'info'); | |
| $this->checkTempDirectories(); | |
| // Gera relatório | |
| $this->generateReport(); | |
| return true; | |
| } | |
| /** | |
| * Verifica se é um diretório Joomla válido | |
| */ | |
| private function isJoomlaDirectory() { | |
| $requiredFiles = [ | |
| 'index.php', | |
| 'configuration.php', | |
| ]; | |
| $requiredDirs = [ | |
| 'administrator', | |
| 'components', | |
| 'modules', | |
| 'plugins', | |
| 'templates', | |
| ]; | |
| foreach ($requiredFiles as $file) { | |
| if (!file_exists(JOOMLA_ROOT . '/' . $file)) { | |
| return false; | |
| } | |
| } | |
| foreach ($requiredDirs as $dir) { | |
| if (!is_dir(JOOMLA_ROOT . '/' . $dir)) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| /** | |
| * Scan recursivo de diretórios | |
| */ | |
| private function scanDirectory($dir) { | |
| $skipDirs = ['.git', '.svn', 'node_modules', 'cache']; | |
| if (!is_dir($dir)) { | |
| return; | |
| } | |
| $items = @scandir($dir); | |
| if (!$items) { | |
| return; | |
| } | |
| foreach ($items as $item) { | |
| if ($item == '.' || $item == '..') { | |
| continue; | |
| } | |
| $path = $dir . '/' . $item; | |
| if (is_dir($path)) { | |
| // Pula diretórios específicos | |
| if (in_array($item, $skipDirs)) { | |
| continue; | |
| } | |
| $this->scanDirectory($path); | |
| } else { | |
| $this->scanFile($path); | |
| } | |
| } | |
| } | |
| /** | |
| * Scan de arquivo individual | |
| */ | |
| private function scanFile($file) { | |
| $this->scannedFiles++; | |
| // Mostra progresso a cada 100 arquivos | |
| if ($this->scannedFiles % 100 == 0) { | |
| $this->log("Arquivos escaneados: {$this->scannedFiles}", 'progress'); | |
| } | |
| // Verifica apenas arquivos PHP | |
| $ext = strtolower(pathinfo($file, PATHINFO_EXTENSION)); | |
| if (!in_array($ext, ['php', 'php3', 'php4', 'php5', 'phtml', 'inc'])) { | |
| return; | |
| } | |
| // Lê o conteúdo do arquivo | |
| $content = @file_get_contents($file); | |
| if ($content === false) { | |
| $this->permissionIssues[] = [ | |
| 'file' => $file, | |
| 'issue' => 'Não foi possível ler o arquivo' | |
| ]; | |
| return; | |
| } | |
| // Verifica assinaturas de malware | |
| $threats = $this->detectMalware($content, $file); | |
| if (!empty($threats)) { | |
| $this->suspiciousFiles[] = [ | |
| 'file' => $file, | |
| 'threats' => $threats, | |
| 'size' => filesize($file), | |
| 'modified' => date('Y-m-d H:i:s', filemtime($file)) | |
| ]; | |
| } | |
| // Verifica arquivos ocultos ou suspeitos | |
| $this->checkSuspiciousFilenames($file); | |
| // Verifica permissões | |
| $this->checkFilePermissions($file); | |
| } | |
| /** | |
| * Detecta malware no conteúdo do arquivo | |
| */ | |
| private function detectMalware($content, $file) { | |
| $threats = []; | |
| foreach ($this->malwareSignatures as $pattern) { | |
| // Usa @ para suprimir warnings de regex inválida e continua | |
| if (@preg_match('/' . $pattern . '/i', $content, $matches)) { | |
| $threats[] = [ | |
| 'pattern' => $pattern, | |
| 'match' => isset($matches[0]) ? substr($matches[0], 0, 100) : '' | |
| ]; | |
| } | |
| } | |
| // Verifica shells conhecidos por conteúdo | |
| if (stripos($content, 'c99shell') !== false) { | |
| $threats[] = ['pattern' => 'c99shell', 'match' => 'C99 Shell detectado']; | |
| } | |
| if (stripos($content, 'r57shell') !== false) { | |
| $threats[] = ['pattern' => 'r57shell', 'match' => 'R57 Shell detectado']; | |
| } | |
| // Verifica funções perigosas múltiplas | |
| $dangerousFunctions = ['eval', 'base64_decode', 'gzinflate', 'system', 'exec']; | |
| $dangerCount = 0; | |
| foreach ($dangerousFunctions as $func) { | |
| if (preg_match_all('/' . $func . '\s*\(/i', $content) > 2) { | |
| $dangerCount++; | |
| } | |
| } | |
| if ($dangerCount >= 3) { | |
| $threats[] = ['pattern' => 'multiple_dangerous', 'match' => 'Múltiplas funções perigosas detectadas']; | |
| } | |
| return $threats; | |
| } | |
| /** | |
| * Verifica nomes de arquivos suspeitos | |
| */ | |
| private function checkSuspiciousFilenames($file) { | |
| $basename = basename($file); | |
| $suspicious = [ | |
| '/^[a-f0-9]{32}\.php$/', // MD5 hash como nome | |
| '/^[a-z]{1,2}\.php$/', // Nomes muito curtos | |
| '/^\d+\.php$/', // Apenas números | |
| '/^(temp|tmp|cache).*\.php$/i', | |
| '/^(shell|backdoor|hack|exploit).*\.php$/i', | |
| '/^\..*\.php$/', // Começa com ponto | |
| '/^(wso|c99|r57|b374k).*\.php$/i', | |
| ]; | |
| foreach ($suspicious as $pattern) { | |
| if (preg_match($pattern, $basename)) { | |
| $this->suspiciousFiles[] = [ | |
| 'file' => $file, | |
| 'threats' => [['pattern' => 'suspicious_filename', 'match' => 'Nome de arquivo suspeito']], | |
| 'size' => filesize($file), | |
| 'modified' => date('Y-m-d H:i:s', filemtime($file)) | |
| ]; | |
| break; | |
| } | |
| } | |
| } | |
| /** | |
| * Verifica permissões de arquivo | |
| */ | |
| private function checkFilePermissions($file) { | |
| $perms = fileperms($file); | |
| $octal = substr(sprintf('%o', $perms), -4); | |
| // Verifica se está gravável pelo mundo | |
| if ($perms & 0x0002) { | |
| $this->permissionIssues[] = [ | |
| 'file' => $file, | |
| 'issue' => "Gravável por todos ({$octal})", | |
| 'permissions' => $octal | |
| ]; | |
| } | |
| // Verifica se está executável | |
| if ($perms & 0x0040) { | |
| $ext = strtolower(pathinfo($file, PATHINFO_EXTENSION)); | |
| if (in_array($ext, ['php', 'php3', 'php4', 'php5', 'phtml'])) { | |
| $this->permissionIssues[] = [ | |
| 'file' => $file, | |
| 'issue' => "Arquivo PHP executável ({$octal})", | |
| 'permissions' => $octal | |
| ]; | |
| } | |
| } | |
| } | |
| /** | |
| * Verifica permissões de arquivos críticos | |
| */ | |
| private function checkPermissions() { | |
| $criticalFiles = [ | |
| 'configuration.php', | |
| '.htaccess', | |
| 'administrator/.htaccess', | |
| ]; | |
| foreach ($criticalFiles as $file) { | |
| $fullPath = JOOMLA_ROOT . '/' . $file; | |
| if (file_exists($fullPath)) { | |
| $perms = fileperms($fullPath); | |
| $octal = substr(sprintf('%o', $perms), -4); | |
| if ($octal != '0444' && $octal != '0644') { | |
| $this->log("AVISO: {$file} tem permissões {$octal} (recomendado: 0444)", 'warning'); | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * Verifica configuration.php | |
| */ | |
| private function checkConfiguration() { | |
| $configFile = JOOMLA_ROOT . '/configuration.php'; | |
| if (!file_exists($configFile)) { | |
| $this->log("ERRO: configuration.php não encontrado!", 'error'); | |
| return; | |
| } | |
| $content = file_get_contents($configFile); | |
| // Verifica se há código suspeito | |
| $threats = $this->detectMalware($content, $configFile); | |
| if (!empty($threats)) { | |
| $this->log("ALERTA: configuration.php contém código suspeito!", 'error'); | |
| $this->suspiciousFiles[] = [ | |
| 'file' => $configFile, | |
| 'threats' => $threats, | |
| 'size' => filesize($configFile), | |
| 'modified' => date('Y-m-d H:i:s', filemtime($configFile)) | |
| ]; | |
| } | |
| // Verifica configurações de segurança | |
| if (preg_match('/public \$debug = \'?1\'?;/', $content)) { | |
| $this->log("AVISO: Debug mode está ativado no configuration.php", 'warning'); | |
| } | |
| if (preg_match('/public \$error_reporting = \'?development\'?;/i', $content)) { | |
| $this->log("AVISO: Error reporting está em modo development", 'warning'); | |
| } | |
| } | |
| /** | |
| * Verifica arquivos .htaccess | |
| */ | |
| private function checkHtaccess() { | |
| $htaccessFiles = [ | |
| JOOMLA_ROOT . '/.htaccess', | |
| JOOMLA_ROOT . '/administrator/.htaccess', | |
| ]; | |
| foreach ($htaccessFiles as $file) { | |
| if (file_exists($file)) { | |
| $content = file_get_contents($file); | |
| // Verifica regras suspeitas | |
| if (preg_match('/RewriteRule.*\.(jpg|jpeg|gif|png|ico).*\.php/i', $content)) { | |
| $this->log("ALERTA: Regra suspeita em {$file} - redireciona imagens para PHP", 'error'); | |
| } | |
| if (preg_match('/auto_prepend_file|auto_append_file/i', $content)) { | |
| $this->log("ALERTA: Configuração suspeita em {$file} - auto_prepend/append_file", 'error'); | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * Verifica diretórios temporários | |
| */ | |
| private function checkTempDirectories() { | |
| $tempDirs = [ | |
| JOOMLA_ROOT . '/tmp', | |
| JOOMLA_ROOT . '/cache', | |
| JOOMLA_ROOT . '/administrator/cache', | |
| ]; | |
| foreach ($tempDirs as $dir) { | |
| if (is_dir($dir)) { | |
| $this->scanDirectory($dir); | |
| } | |
| } | |
| } | |
| /** | |
| * Gera relatório final | |
| */ | |
| private function generateReport() { | |
| $endTime = microtime(true); | |
| $duration = round($endTime - $this->startTime, 2); | |
| $this->log("", 'info'); | |
| $this->log("=== RELATÓRIO FINAL ===", 'info'); | |
| $this->log("Tempo de execução: {$duration} segundos", 'info'); | |
| $this->log("Total de arquivos escaneados: {$this->scannedFiles}", 'info'); | |
| $this->log("", 'info'); | |
| // Arquivos suspeitos | |
| $this->log("ARQUIVOS SUSPEITOS ENCONTRADOS: " . count($this->suspiciousFiles), 'info'); | |
| if (!empty($this->suspiciousFiles)) { | |
| foreach ($this->suspiciousFiles as $item) { | |
| $this->log("", 'warning'); | |
| $this->log("Arquivo: " . str_replace(JOOMLA_ROOT, '', $item['file']), 'warning'); | |
| $this->log("Tamanho: " . $item['size'] . " bytes", 'warning'); | |
| $this->log("Modificado: " . $item['modified'], 'warning'); | |
| $this->log("Ameaças detectadas:", 'warning'); | |
| foreach ($item['threats'] as $threat) { | |
| $this->log(" - Padrão: {$threat['pattern']}", 'warning'); | |
| if (!empty($threat['match'])) { | |
| $this->log(" Match: " . htmlspecialchars(substr($threat['match'], 0, 100)), 'warning'); | |
| } | |
| } | |
| } | |
| } else { | |
| $this->log("Nenhum arquivo suspeito detectado!", 'success'); | |
| } | |
| $this->log("", 'info'); | |
| // Problemas de permissão | |
| $this->log("PROBLEMAS DE PERMISSÃO: " . count($this->permissionIssues), 'info'); | |
| if (!empty($this->permissionIssues)) { | |
| $shown = 0; | |
| foreach ($this->permissionIssues as $item) { | |
| if ($shown >= 20) { | |
| $remaining = count($this->permissionIssues) - $shown; | |
| $this->log("... e mais {$remaining} problemas de permissão", 'info'); | |
| break; | |
| } | |
| $this->log("Arquivo: " . str_replace(JOOMLA_ROOT, '', $item['file']), 'warning'); | |
| $this->log("Problema: " . $item['issue'], 'warning'); | |
| $shown++; | |
| } | |
| } else { | |
| $this->log("Nenhum problema de permissão crítico detectado!", 'success'); | |
| } | |
| $this->log("", 'info'); | |
| // Recomendações | |
| $this->log("=== RECOMENDAÇÕES ===", 'info'); | |
| $this->log("1. Investigue todos os arquivos suspeitos listados acima", 'info'); | |
| $this->log("2. Faça backup antes de remover qualquer arquivo", 'info'); | |
| $this->log("3. Mantenha o Joomla e extensões sempre atualizados", 'info'); | |
| $this->log("4. Use senhas fortes e autenticação de dois fatores", 'info'); | |
| $this->log("5. Configure permissões adequadas (644 para arquivos, 755 para diretórios)", 'info'); | |
| $this->log("6. Considere usar extensões de segurança como Akeeba Admin Tools ou RSFirewall", 'info'); | |
| // Salva relatório em arquivo | |
| $this->saveReport(); | |
| } | |
| /** | |
| * Salva relatório em arquivo | |
| */ | |
| private function saveReport() { | |
| $reportFile = JOOMLA_ROOT . '/security-scan-' . date('Y-m-d-His') . '.txt'; | |
| ob_start(); | |
| echo "=== JOOMLA SECURITY SCAN REPORT ===\n"; | |
| echo "Data: " . date('Y-m-d H:i:s') . "\n"; | |
| echo "Diretório: " . JOOMLA_ROOT . "\n\n"; | |
| echo "RESUMO:\n"; | |
| echo "- Arquivos escaneados: {$this->scannedFiles}\n"; | |
| echo "- Arquivos suspeitos: " . count($this->suspiciousFiles) . "\n"; | |
| echo "- Problemas de permissão: " . count($this->permissionIssues) . "\n\n"; | |
| if (!empty($this->suspiciousFiles)) { | |
| echo "ARQUIVOS SUSPEITOS:\n"; | |
| echo str_repeat("=", 80) . "\n"; | |
| foreach ($this->suspiciousFiles as $item) { | |
| echo "\nArquivo: {$item['file']}\n"; | |
| echo "Tamanho: {$item['size']} bytes\n"; | |
| echo "Modificado: {$item['modified']}\n"; | |
| echo "Ameaças:\n"; | |
| foreach ($item['threats'] as $threat) { | |
| echo " - {$threat['pattern']}\n"; | |
| if (!empty($threat['match'])) { | |
| echo " " . substr($threat['match'], 0, 100) . "\n"; | |
| } | |
| } | |
| } | |
| } | |
| if (!empty($this->permissionIssues)) { | |
| echo "\n\nPROBLEMAS DE PERMISSÃO:\n"; | |
| echo str_repeat("=", 80) . "\n"; | |
| foreach ($this->permissionIssues as $item) { | |
| echo "{$item['file']} - {$item['issue']}\n"; | |
| } | |
| } | |
| $reportContent = ob_get_clean(); | |
| file_put_contents($reportFile, $reportContent); | |
| $this->log("", 'info'); | |
| $this->log("Relatório completo salvo em: {$reportFile}", 'success'); | |
| } | |
| /** | |
| * Log de mensagens | |
| */ | |
| private function log($message, $type = 'info') { | |
| $colors = [ | |
| 'info' => "\033[0m", // Normal | |
| 'success' => "\033[32m", // Verde | |
| 'warning' => "\033[33m", // Amarelo | |
| 'error' => "\033[31m", // Vermelho | |
| 'progress' => "\033[36m", // Ciano | |
| ]; | |
| $reset = "\033[0m"; | |
| // Se não estiver no CLI, não usa cores | |
| if (php_sapi_name() !== 'cli') { | |
| echo htmlspecialchars($message) . "<br>\n"; | |
| } else { | |
| $color = isset($colors[$type]) ? $colors[$type] : $colors['info']; | |
| echo $color . $message . $reset . "\n"; | |
| } | |
| flush(); | |
| } | |
| } | |
| // Execução | |
| if (php_sapi_name() === 'cli') { | |
| // Modo CLI | |
| echo "\n"; | |
| $scanner = new JoomlaSecurityScanner(); | |
| $scanner->scan(); | |
| echo "\n"; | |
| } else { | |
| // Modo web - adiciona proteção básica | |
| // DESCOMENTE AS LINHAS ABAIXO E CONFIGURE UMA SENHA | |
| /* | |
| session_start(); | |
| $validPassword = 'SUA_SENHA_AQUI'; // MUDE ISSO! | |
| if (!isset($_SESSION['authenticated'])) { | |
| if (isset($_POST['password']) && $_POST['password'] === $validPassword) { | |
| $_SESSION['authenticated'] = true; | |
| } else { | |
| ?> | |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <title>Joomla Security Scanner - Login</title> | |
| <style> | |
| body { font-family: Arial, sans-serif; margin: 50px; } | |
| input { padding: 5px; margin: 5px; } | |
| </style> | |
| </head> | |
| <body> | |
| <h2>Joomla Security Scanner</h2> | |
| <form method="post"> | |
| <input type="password" name="password" placeholder="Senha" required> | |
| <button type="submit">Acessar</button> | |
| </form> | |
| </body> | |
| </html> | |
| <?php | |
| exit; | |
| } | |
| } | |
| */ | |
| // HTML output | |
| ?> | |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <title>Joomla Security Scanner</title> | |
| <meta charset="UTF-8"> | |
| <style> | |
| body { | |
| font-family: 'Courier New', monospace; | |
| background: #1e1e1e; | |
| color: #d4d4d4; | |
| padding: 20px; | |
| line-height: 1.6; | |
| } | |
| .container { | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| background: #252526; | |
| padding: 20px; | |
| border-radius: 5px; | |
| } | |
| h1 { | |
| color: #4ec9b0; | |
| border-bottom: 2px solid #4ec9b0; | |
| padding-bottom: 10px; | |
| } | |
| .info { color: #d4d4d4; } | |
| .success { color: #4ec9b0; } | |
| .warning { color: #dcdcaa; } | |
| .error { color: #f48771; } | |
| .progress { color: #9cdcfe; } | |
| pre { | |
| background: #1e1e1e; | |
| padding: 15px; | |
| border-radius: 3px; | |
| overflow-x: auto; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h1>🔍 Joomla Security Scanner</h1> | |
| <pre><?php | |
| $scanner = new JoomlaSecurityScanner(); | |
| $scanner->scan(); | |
| ?></pre> | |
| </div> | |
| </body> | |
| </html> | |
| <?php | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment