File manager - Edit - /opt/app-version-detector/app-version-detector.phar
Back
<?php if (in_array('phar', stream_get_wrappers()) && class_exists('Phar', 0)) { Phar::interceptFileFuncs(); set_include_path('phar://' . __FILE__ . PATH_SEPARATOR . get_include_path()); include 'phar://' . __FILE__ . '/' . Extract_Phar::START; return; } class Extract_Phar { static $temp; static $origdir; const GZ = 0x1000; const BZ2 = 0x2000; const MASK = 0x3000; const START = 'bin/cli.php'; const LEN = 6636; static function go($return = false) { $fp = fopen(__FILE__, 'rb'); fseek($fp, self::LEN); $L = unpack('V', $a = fread($fp, 4)); $m = ''; do { $read = 8192; if ($L[1] - strlen($m) < 8192) { $read = $L[1] - strlen($m); } $last = fread($fp, $read); $m .= $last; } while (strlen($last) && strlen($m) < $L[1]); if (strlen($m) < $L[1]) { die('ERROR: manifest length read was "' . strlen($m) .'" should be "' . $L[1] . '"'); } $info = self::_unpack($m); $f = $info['c']; if ($f & self::GZ) { if (!function_exists('gzinflate')) { die('Error: zlib extension is not enabled -' . ' gzinflate() function needed for zlib-compressed .phars'); } } if ($f & self::BZ2) { if (!function_exists('bzdecompress')) { die('Error: bzip2 extension is not enabled -' . ' bzdecompress() function needed for bz2-compressed .phars'); } } $temp = self::tmpdir(); if (!$temp || !is_writable($temp)) { $sessionpath = session_save_path(); if (strpos ($sessionpath, ";") !== false) $sessionpath = substr ($sessionpath, strpos ($sessionpath, ";")+1); if (!file_exists($sessionpath) || !is_dir($sessionpath)) { die('Could not locate temporary directory to extract phar'); } $temp = $sessionpath; } // Create a private, unguessable extraction directory owned by the // current user. Using a fixed, world-writable /tmp/pharextract/<name> // path with an attacker-computable md5 marker as the sole reuse gate // allowed a local user to pre-plant the directory (cache poisoning of // bin/cli.php) or plant symlinks the extraction would write through. // A fresh random 0700 directory per run, verified to be a real // directory we own and not a symlink, closes that TOCTOU window. // // The parent ($base) is per-euid (e.g. /tmp/pharextract-0 for root) // and 0700 owned by the current user, so an unprivileged attacker // cannot rename or replace entries inside it after creation. // // posix_geteuid is required: without it $euid would silently collapse // to 0 for every caller AND isOwnedDir would skip the ownership // comparison, letting an attacker who pre-creates the base dir pass // the trust check trivially. There is no safe fallback — hard-fail. if (!function_exists('posix_geteuid')) { die('posix_geteuid is required for safe phar extraction (PHP built without --enable-posix?)'); } $euid = posix_geteuid(); $base = $temp . '/pharextract-' . $euid; @mkdir($base, 0700); if (!self::isOwnedDir($base)) { die('Refusing to use untrusted pharextract base directory'); } $temp = self::makePrivateTempDir($base, basename(__FILE__, '.phar')); if ($temp === false) { die('Could not create a private temporary directory to extract phar'); } self::$temp = $temp; self::$origdir = getcwd(); foreach ($info['m'] as $path => $file) { // Defense in depth: even with a per-euid 0700 $base, re-verify // the extraction root is still our owned non-symlink directory // before every write so a parent-dir hijack between iterations // can't redirect the extraction. if (!self::isOwnedDir($temp)) { die('Extraction root was replaced — aborting'); } self::assertSafePath($temp, $path); @mkdir(dirname($temp . '/' . $path), 0700, true); clearstatcache(); if ($path[strlen($path) - 1] == '/') { @mkdir($temp . '/' . $path, 0700); } else { self::writeFileNoFollow($temp . '/' . $path, self::extractFile($path, $file, $fp)); @chmod($temp . '/' . $path, 0600); } } chdir($temp); if (!$return) { include self::START; } } // Create a fresh, unguessable, 0700 directory under $base owned by the // current effective user. Verifies the result is a real directory (not a // symlink) owned by us before returning it. static function makePrivateTempDir($base, $name) { for ($i = 0; $i < 16; $i++) { // Require a real CSPRNG. mt_rand is seeded predictably and would // make the per-run dir name guessable, defeating the unguessable- // suffix protection that the per-euid $base also leans on. if (function_exists('random_bytes')) { $rnd = bin2hex(random_bytes(16)); } elseif (function_exists('openssl_random_pseudo_bytes')) { $bytes = openssl_random_pseudo_bytes(16, $strong); if (!$strong) { die('No CSPRNG available for extraction directory name'); } $rnd = bin2hex($bytes); } else { die('No CSPRNG available for extraction directory name'); } $dir = $base . '/' . $name . '.' . $rnd; clearstatcache(); if (file_exists($dir) || is_link($dir)) { continue; } if (!@mkdir($dir, 0700)) { continue; } clearstatcache(); if (!self::isOwnedDir($dir)) { @rmdir($dir); continue; } return $dir; } return false; } // True only if $dir is a real directory (not a symlink) owned by the // current effective uid. go() already hard-fails if posix_geteuid is // unavailable, so this helper assumes it can be called unconditionally; // skipping the uid check on absent POSIX would silently disable the // ownership gate and let an attacker-owned pre-created dir pass. static function isOwnedDir($dir) { if (is_link($dir) || !is_dir($dir)) { return false; } $stat = @lstat($dir); if ($stat === false) { return false; } if ($stat['uid'] !== posix_geteuid()) { return false; } return true; } // Reject manifest paths that try to escape the extraction root via // absolute paths or ".." path segments, and refuse to write through a // pre-existing symlink anywhere along the path. // // ".." is checked per path segment (not as a substring) so legitimate // archive entries containing two consecutive dots in a filename (e.g. // "foo..bar") are not rejected. static function assertSafePath($temp, $path) { if ($path === '' || $path[0] === '/' || $path[0] === '\\' || strpos($path, "\0") !== false) { die('Invalid internal .phar path'); } $parts = explode('/', $path); $cur = $temp; foreach ($parts as $p) { if ($p === '') { continue; } if ($p === '.' || $p === '..') { die('Invalid internal .phar path'); } $cur .= '/' . $p; clearstatcache(); if (is_link($cur)) { die('Refusing to extract through symlink'); } } } // Write data without following a final symlink: open with the 'x' (O_EXCL // | O_CREAT) flag so an attacker-planted symlink at the target causes the // open to fail rather than being written through. Loop on fwrite() since // it may write fewer bytes than requested in a single call, which would // otherwise leave a truncated PHP file on disk that the stub later // include()'s. static function writeFileNoFollow($file, $data) { clearstatcache(); if (is_link($file)) { die('Refusing to extract over symlink'); } $fp = @fopen($file, 'xb'); if ($fp === false) { die('Could not safely create extracted file'); } $remaining = strlen($data); $offset = 0; while ($remaining > 0) { $chunk = $remaining === strlen($data) && $offset === 0 ? $data : substr($data, $offset, $remaining); $written = @fwrite($fp, $chunk); if ($written === false || $written === 0) { fclose($fp); @unlink($file); die('Could not write all bytes to extracted file'); } $offset += $written; $remaining -= $written; } fclose($fp); } static function tmpdir() { if (strpos(PHP_OS, 'WIN') !== false) { if ($var = getenv('TMP') ? getenv('TMP') : getenv('TEMP')) { return $var; } if (is_dir('/temp') || mkdir('/temp')) { return realpath('/temp'); } return false; } if ($var = getenv('TMPDIR')) { return $var; } return realpath('/tmp'); } static function _unpack($m) { $info = unpack('V', substr($m, 0, 4)); $l = unpack('V', substr($m, 10, 4)); $m = substr($m, 14 + $l[1]); $s = unpack('V', substr($m, 0, 4)); $o = 0; $start = 4 + $s[1]; $ret['c'] = 0; for ($i = 0; $i < $info[1]; $i++) { $len = unpack('V', substr($m, $start, 4)); $start += 4; $savepath = substr($m, $start, $len[1]); $start += $len[1]; $ret['m'][$savepath] = array_values(unpack('Va/Vb/Vc/Vd/Ve/Vf', substr($m, $start, 24))); $ret['m'][$savepath][3] = sprintf('%u', $ret['m'][$savepath][3] & 0xffffffff); $ret['m'][$savepath][7] = $o; $o += $ret['m'][$savepath][2]; $start += 24 + $ret['m'][$savepath][5]; $ret['c'] |= $ret['m'][$savepath][4] & self::MASK; } return $ret; } static function extractFile($path, $entry, $fp) { $data = ''; $c = $entry[2]; while ($c) { if ($c < 8192) { $data .= @fread($fp, $c); $c = 0; } else { $c -= 8192; $data .= @fread($fp, 8192); } } if ($entry[4] & self::GZ) { $data = gzinflate($data); } elseif ($entry[4] & self::BZ2) { $data = bzdecompress($data); } if (strlen($data) != $entry[0]) { die("Invalid internal .phar file (size error " . strlen($data) . " != " . $stat[7] . ")"); } if ($entry[3] != sprintf("%u", crc32($data) & 0xffffffff)) { die("Invalid internal .phar file (checksum error)"); } return $data; } static function _removeTmpFiles($temp, $origdir) { chdir($temp); foreach (glob('*') as $f) { if (file_exists($f)) { is_dir($f) ? @rmdir($f) : @unlink($f); if (file_exists($f) && is_dir($f)) { self::_removeTmpFiles($f, getcwd()); } } } @rmdir($temp); clearstatcache(); chdir($origdir); } } Extract_Phar::go(); __HALT_COMPILER(); ?> Z app-version-detector.phar % vendor/composer/InstalledVersions.php�C ��;j�C <nw� ! vendor/composer/autoload_psr4.phpF ��;jF 1F� vendor/composer/LICENSE. ��;j. �� $ vendor/composer/semver/composer.jsonL ��;jL P���� vendor/composer/semver/LICENSE ��;j Bh� vendor/composer/semver/README.mdk ��;jk O� � # vendor/composer/semver/CHANGELOG.md� ��;j� Mgˤ , vendor/composer/semver/src/VersionParser.phpKT ��;jKT �v�ʤ ) vendor/composer/semver/src/Comparator.php� ��;j� �g3 � 9 vendor/composer/semver/src/Constraint/MultiConstraint.php< ��;j< ª� � = vendor/composer/semver/src/Constraint/ConstraintInterface.php^ ��;j^ )��X� <