????
| Current Path : /opt/app-version-detector/ |
| Current File : //opt/app-version-detector/app-version-detector.phar |
<?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� <