647 lines
20 KiB
PHP
647 lines
20 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
header('Content-Type: text/plain; charset=utf-8');
|
|
|
|
$SCORE = ['PASS' => 0, 'WARN' => 0, 'FAIL' => 0];
|
|
$FINDINGS = [];
|
|
|
|
function add_result(string $level, string $title, string $detail = ''): void {
|
|
global $SCORE, $FINDINGS;
|
|
if (!isset($SCORE[$level])) {
|
|
$level = 'WARN';
|
|
}
|
|
$SCORE[$level]++;
|
|
$FINDINGS[] = [$level, $title, $detail];
|
|
}
|
|
|
|
function line(string $label, $value = null): void {
|
|
if ($value === null) {
|
|
echo $label . PHP_EOL;
|
|
return;
|
|
}
|
|
if (is_bool($value)) {
|
|
$value = $value ? 'YES' : 'NO';
|
|
} elseif (is_array($value) || is_object($value)) {
|
|
$value = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
|
}
|
|
echo str_pad($label, 46) . ': ' . $value . PHP_EOL;
|
|
}
|
|
|
|
function section(string $title): void {
|
|
echo PHP_EOL . "--- {$title} ---" . PHP_EOL;
|
|
}
|
|
|
|
function bytes_from_ini(?string $val): ?int {
|
|
if ($val === null || $val === '') return null;
|
|
$val = trim($val);
|
|
if ($val === '-1') return -1;
|
|
$last = strtolower(substr($val, -1));
|
|
$num = (float)$val;
|
|
return match($last) {
|
|
'g' => (int)($num * 1024 * 1024 * 1024),
|
|
'm' => (int)($num * 1024 * 1024),
|
|
'k' => (int)($num * 1024),
|
|
default => (int)$num,
|
|
};
|
|
}
|
|
|
|
function with_error_capture(callable $fn): array {
|
|
$error = null;
|
|
set_error_handler(function($severity, $message) use (&$error) {
|
|
$error = $message;
|
|
return true;
|
|
});
|
|
try {
|
|
$result = $fn();
|
|
restore_error_handler();
|
|
return ['result' => $result, 'error' => $error];
|
|
} catch (Throwable $e) {
|
|
restore_error_handler();
|
|
return ['result' => null, 'error' => $e->getMessage()];
|
|
}
|
|
}
|
|
|
|
function try_read_file(string $path): array {
|
|
return with_error_capture(function() use ($path) {
|
|
$data = @file_get_contents($path);
|
|
if ($data === false) return false;
|
|
return 'len=' . strlen($data);
|
|
}) + ['path' => $path];
|
|
}
|
|
|
|
function try_scandir_path(string $path): array {
|
|
return with_error_capture(function() use ($path) {
|
|
$data = @scandir($path);
|
|
if ($data === false) return false;
|
|
return array_slice($data, 0, 15);
|
|
}) + ['path' => $path];
|
|
}
|
|
|
|
function try_socket(string $target, int $port, float $timeout = 1.2): array {
|
|
$errno = 0;
|
|
$errstr = '';
|
|
$fp = @fsockopen($target, $port, $errno, $errstr, $timeout);
|
|
$ok = is_resource($fp);
|
|
if ($ok) fclose($fp);
|
|
return [
|
|
'target' => $target,
|
|
'port' => $port,
|
|
'connected' => $ok,
|
|
'errno' => $errno,
|
|
'errstr' => $errstr,
|
|
];
|
|
}
|
|
|
|
function try_unix_socket(string $path, float $timeout = 1.0): array {
|
|
$errno = 0;
|
|
$errstr = '';
|
|
$fp = @stream_socket_client('unix://' . $path, $errno, $errstr, $timeout);
|
|
$ok = is_resource($fp);
|
|
if ($ok) fclose($fp);
|
|
return [
|
|
'path' => $path,
|
|
'connected' => $ok,
|
|
'errno' => $errno,
|
|
'errstr' => $errstr,
|
|
];
|
|
}
|
|
|
|
echo "=== SHARED HOSTING SAFE AUDIT v2.1 ===" . PHP_EOL;
|
|
echo "Time: " . date('c') . PHP_EOL;
|
|
|
|
$docRoot = realpath($_SERVER['DOCUMENT_ROOT'] ?? getcwd()) ?: getcwd();
|
|
$scriptFile = $_SERVER['SCRIPT_FILENAME'] ?? __FILE__;
|
|
$baseTmp = $docRoot . '/.audit_tmp_' . getmypid() . '_' . mt_rand(1000, 9999);
|
|
@mkdir($baseTmp, 0700, true);
|
|
|
|
section('Runtime identity');
|
|
line('PHP version', PHP_VERSION);
|
|
line('SAPI', PHP_SAPI);
|
|
line('OS', PHP_OS_FAMILY);
|
|
line('Document root', $docRoot);
|
|
line('Script filename', $scriptFile);
|
|
line('Current dir', getcwd());
|
|
line('Loaded php.ini', php_ini_loaded_file() ?: 'none');
|
|
line('Additional .ini files', php_ini_scanned_files() ?: 'none');
|
|
line('Hostname', php_uname('n'));
|
|
line('Server software', $_SERVER['SERVER_SOFTWARE'] ?? 'n/a');
|
|
line('Server addr', $_SERVER['SERVER_ADDR'] ?? 'n/a');
|
|
line('Server port', $_SERVER['SERVER_PORT'] ?? 'n/a');
|
|
line('Remote addr', $_SERVER['REMOTE_ADDR'] ?? 'n/a');
|
|
|
|
section('PHP limits and config');
|
|
$iniKeys = [
|
|
'memory_limit',
|
|
'max_execution_time',
|
|
'max_input_time',
|
|
'max_input_vars',
|
|
'post_max_size',
|
|
'upload_max_filesize',
|
|
'open_basedir',
|
|
'disable_functions',
|
|
'disable_classes',
|
|
'user_ini.filename',
|
|
'user_ini.cache_ttl',
|
|
'file_uploads',
|
|
'allow_url_fopen',
|
|
'allow_url_include',
|
|
'session.save_path',
|
|
'upload_tmp_dir',
|
|
'sys_temp_dir',
|
|
'display_errors',
|
|
'log_errors',
|
|
'expose_php',
|
|
'mail.add_x_header',
|
|
'mysqli.default_socket',
|
|
'pdo_mysql.default_socket',
|
|
];
|
|
foreach ($iniKeys as $k) {
|
|
line($k, ini_get($k));
|
|
}
|
|
|
|
section('Dangerous functions present');
|
|
$dangerFns = [
|
|
'exec','shell_exec','system','passthru','proc_open','popen',
|
|
'pcntl_exec','pcntl_fork','putenv','mail','symlink','link',
|
|
'stream_socket_client','fsockopen'
|
|
];
|
|
foreach ($dangerFns as $fn) {
|
|
line("function_exists($fn)", function_exists($fn));
|
|
}
|
|
|
|
section('Loaded extensions');
|
|
$exts = ['mysqli','pdo_mysql','redis','ftp','curl','openssl','pcntl','posix','sockets','imap','intl'];
|
|
foreach ($exts as $ext) {
|
|
line("extension_loaded($ext)", extension_loaded($ext));
|
|
}
|
|
|
|
section('Filesystem isolation');
|
|
$fsTests = [
|
|
$docRoot,
|
|
$docRoot . '/../',
|
|
$docRoot . '/../../',
|
|
'/var/www',
|
|
'/var/www/vhosts',
|
|
'/etc/passwd',
|
|
'/etc/hosts',
|
|
'/proc/self/environ',
|
|
'/proc/meminfo',
|
|
'/tmp',
|
|
'/var/tmp',
|
|
'/run',
|
|
'/run/php',
|
|
'/run/mysqld',
|
|
'/dev/shm',
|
|
'/var/spool/postfix',
|
|
];
|
|
foreach ($fsTests as $path) {
|
|
$isDir = @is_dir($path);
|
|
$result = $isDir ? try_scandir_path($path) : try_read_file($path);
|
|
line($path, $result);
|
|
}
|
|
|
|
section('Path traversal / realpath checks');
|
|
$traversalTests = [
|
|
$docRoot . '/../www',
|
|
$docRoot . '/../tmp',
|
|
$docRoot . '/../session',
|
|
$docRoot . '/../../../../etc/passwd',
|
|
$docRoot . '/../other-vhost/www',
|
|
];
|
|
foreach ($traversalTests as $path) {
|
|
line("realpath($path)", @realpath($path) ?: 'false');
|
|
line("read($path)", try_read_file($path));
|
|
}
|
|
|
|
section('Allowed path write tests');
|
|
$writeFile = $baseTmp . '/write-test.txt';
|
|
$res = with_error_capture(function() use ($writeFile) {
|
|
return file_put_contents($writeFile, "audit\n");
|
|
});
|
|
line('Write file in docroot tmp', ['path' => $writeFile] + $res);
|
|
line('File exists after write', file_exists($writeFile));
|
|
line('File readable after write', is_readable($writeFile));
|
|
line('File writable after write', is_writable($writeFile));
|
|
|
|
$renameTo = $baseTmp . '/write-test-renamed.txt';
|
|
$res = with_error_capture(function() use ($writeFile, $renameTo) {
|
|
return @rename($writeFile, $renameTo);
|
|
});
|
|
line('Rename inside allowed path', ['from' => $writeFile, 'to' => $renameTo] + $res);
|
|
|
|
$copyToSession = dirname($docRoot) . '/session/audit-copy.txt';
|
|
$res = with_error_capture(function() use ($renameTo, $copyToSession) {
|
|
return @copy($renameTo, $copyToSession);
|
|
});
|
|
line('Copy from docroot to session dir', ['to' => $copyToSession] + $res);
|
|
|
|
section('Symlink / hardlink inside allowed path');
|
|
$src = $baseTmp . '/src.txt';
|
|
@file_put_contents($src, 'x');
|
|
$symlinkTarget = $baseTmp . '/sym.txt';
|
|
$hardlinkTarget = $baseTmp . '/hard.txt';
|
|
$symlinkRes = with_error_capture(fn() => @symlink($src, $symlinkTarget));
|
|
$hardlinkRes = with_error_capture(fn() => @link($src, $hardlinkTarget));
|
|
line('Symlink allowed path', $symlinkRes);
|
|
line('Hardlink allowed path', $hardlinkRes);
|
|
line('Symlink exists', is_link($symlinkTarget));
|
|
line('Hardlink exists', file_exists($hardlinkTarget));
|
|
|
|
section('Runtime override attempts');
|
|
$overrideTests = [
|
|
'memory_limit' => '2048M',
|
|
'max_execution_time' => '600',
|
|
'upload_max_filesize' => '2048M',
|
|
'post_max_size' => '2048M',
|
|
'open_basedir' => '/',
|
|
];
|
|
$overrideResults = [];
|
|
foreach ($overrideTests as $key => $value) {
|
|
$before = ini_get($key);
|
|
$ret = @ini_set($key, $value);
|
|
$after = ini_get($key);
|
|
$overrideResults[$key] = [
|
|
'before' => $before,
|
|
'return' => $ret,
|
|
'after' => $after,
|
|
'changed' => ($before !== $after),
|
|
];
|
|
line("ini_set($key)", $overrideResults[$key]);
|
|
}
|
|
|
|
section('Execution capability tests');
|
|
$execResults = [];
|
|
|
|
if (function_exists('exec')) {
|
|
$execResults['exec'] = with_error_capture(function() {
|
|
$out = [];
|
|
$rc = 0;
|
|
@exec('id 2>&1', $out, $rc);
|
|
return ['rc' => $rc, 'out' => implode("\n", array_slice($out, 0, 5))];
|
|
});
|
|
line('exec("id")', $execResults['exec']);
|
|
}
|
|
|
|
if (function_exists('shell_exec')) {
|
|
$execResults['shell_exec'] = with_error_capture(function() {
|
|
$out = @shell_exec('whoami 2>&1');
|
|
return $out === null ? null : trim($out);
|
|
});
|
|
line('shell_exec("whoami")', $execResults['shell_exec']);
|
|
}
|
|
|
|
if (function_exists('system')) {
|
|
$execResults['system'] = with_error_capture(function() {
|
|
ob_start();
|
|
$rc = 0;
|
|
@system('pwd 2>&1', $rc);
|
|
$out = ob_get_clean();
|
|
return ['rc' => $rc, 'out' => trim((string)$out)];
|
|
});
|
|
line('system("pwd")', $execResults['system']);
|
|
}
|
|
|
|
if (function_exists('proc_open')) {
|
|
$execResults['proc_open'] = with_error_capture(function() {
|
|
$desc = [
|
|
0 => ['pipe', 'r'],
|
|
1 => ['pipe', 'w'],
|
|
2 => ['pipe', 'w'],
|
|
];
|
|
$proc = @proc_open('id', $desc, $pipes);
|
|
if (!is_resource($proc)) return 'proc_open failed';
|
|
fclose($pipes[0]);
|
|
$stdout = stream_get_contents($pipes[1]);
|
|
$stderr = stream_get_contents($pipes[2]);
|
|
fclose($pipes[1]);
|
|
fclose($pipes[2]);
|
|
$code = proc_close($proc);
|
|
return ['rc' => $code, 'stdout' => trim($stdout), 'stderr' => trim($stderr)];
|
|
});
|
|
line('proc_open("id")', $execResults['proc_open']);
|
|
}
|
|
|
|
if (function_exists('popen')) {
|
|
$execResults['popen'] = with_error_capture(function() {
|
|
$h = @popen('id 2>&1', 'r');
|
|
if (!is_resource($h)) return 'popen failed';
|
|
$out = stream_get_contents($h);
|
|
$rc = pclose($h);
|
|
return ['rc' => $rc, 'out' => trim((string)$out)];
|
|
});
|
|
line('popen("id")', $execResults['popen']);
|
|
}
|
|
|
|
section('Fork capability');
|
|
line('extension_loaded(pcntl)', extension_loaded('pcntl'));
|
|
line('function_exists(pcntl_fork)', function_exists('pcntl_fork'));
|
|
line('Active fork test', 'SKIPPED in web SAPI for safety');
|
|
|
|
section('Controlled memory probe');
|
|
$memoryLimit = bytes_from_ini(ini_get('memory_limit'));
|
|
$chunks = [];
|
|
$allocated = 0;
|
|
$chunkSize = 4 * 1024 * 1024;
|
|
$target = ($memoryLimit !== null && $memoryLimit > 0) ? (int)($memoryLimit * 0.70) : 64 * 1024 * 1024;
|
|
$oom = false;
|
|
$oomMsg = null;
|
|
try {
|
|
while ($allocated + $chunkSize <= $target) {
|
|
$chunks[] = str_repeat('A', $chunkSize);
|
|
$allocated += $chunkSize;
|
|
}
|
|
} catch (Throwable $e) {
|
|
$oom = true;
|
|
$oomMsg = $e->getMessage();
|
|
}
|
|
line('memory_limit parsed', $memoryLimit);
|
|
line('allocated safely', $allocated);
|
|
line('oom caught', $oom);
|
|
line('oom message', $oomMsg ?: 'none');
|
|
unset($chunks);
|
|
|
|
section('Controlled CPU / timeout probe');
|
|
$start = microtime(true);
|
|
$iterations = 0;
|
|
$limitSeconds = max(1, (int)ini_get('max_execution_time'));
|
|
$softBudget = min(3, max(1, $limitSeconds - 1));
|
|
while ((microtime(true) - $start) < $softBudget) {
|
|
hash('sha256', random_bytes(256), false);
|
|
$iterations++;
|
|
}
|
|
line('elapsed', round(microtime(true) - $start, 3) . 's');
|
|
line('iterations', $iterations);
|
|
line('soft budget', $softBudget . 's');
|
|
|
|
section('set_time_limit test');
|
|
$setTimeLimitRes = with_error_capture(function() {
|
|
return @set_time_limit(600);
|
|
});
|
|
line('set_time_limit(600)', $setTimeLimitRes);
|
|
line('max_execution_time after set_time_limit', ini_get('max_execution_time'));
|
|
|
|
section('Network reachability');
|
|
$netTargets = [
|
|
['127.0.0.1', 25],
|
|
['127.0.0.1', 3306],
|
|
['127.0.0.1', 6379],
|
|
['127.0.0.1', 11211],
|
|
['127.0.0.1', 7080],
|
|
['127.0.0.1', 80],
|
|
['127.0.0.1', 443],
|
|
];
|
|
$netResults = [];
|
|
foreach ($netTargets as [$host, $port]) {
|
|
$netResults["$host:$port"] = try_socket($host, $port);
|
|
line("$host:$port", $netResults["$host:$port"]);
|
|
}
|
|
|
|
section('Unix socket reachability');
|
|
$unixCandidates = [
|
|
'/run/mysqld/mysqld.sock',
|
|
'/var/run/mysqld/mysqld.sock',
|
|
'/tmp/mysql.sock',
|
|
'/run/redis/redis-server.sock',
|
|
'/var/run/redis/redis.sock',
|
|
'/tmp/redis.sock',
|
|
'/usr/local/lsws/admin/tmp/admin.sock',
|
|
];
|
|
$unixResults = [];
|
|
foreach ($unixCandidates as $sock) {
|
|
$unixResults[$sock] = try_unix_socket($sock);
|
|
line($sock, $unixResults[$sock]);
|
|
}
|
|
|
|
section('Stream wrappers');
|
|
$wrappers = stream_get_wrappers();
|
|
sort($wrappers);
|
|
line('wrappers', $wrappers);
|
|
|
|
$wrapperReads = [
|
|
'php://memory',
|
|
'php://temp',
|
|
'data://text/plain;base64,SGVsbG8=',
|
|
];
|
|
foreach ($wrapperReads as $wrapper) {
|
|
line("read $wrapper", with_error_capture(function() use ($wrapper) {
|
|
$d = @file_get_contents($wrapper);
|
|
if ($d === false) return false;
|
|
return 'len=' . strlen($d) . ' data=' . substr($d, 0, 40);
|
|
}));
|
|
}
|
|
|
|
section('Include wrapper tests');
|
|
$includeFile = $baseTmp . '/include-test.php';
|
|
file_put_contents($includeFile, "<?php return ['ok' => true, 'time' => time()];");
|
|
$includeLocal = with_error_capture(function() use ($includeFile) {
|
|
return include $includeFile;
|
|
});
|
|
$includeData = with_error_capture(function() {
|
|
return @include 'data://text/plain;base64,PD9waHAgcmV0dXJuIFsiZGF0YSI9PnRydWVdOw==';
|
|
});
|
|
line('include local file', $includeLocal);
|
|
line('include data:// wrapper', $includeData);
|
|
|
|
section('Environment leakage');
|
|
$envKeys = ['HOME','USER','LOGNAME','PATH','TMPDIR','TEMP','HOSTNAME'];
|
|
$envOut = [];
|
|
foreach ($envKeys as $k) {
|
|
$envOut[$k] = getenv($k);
|
|
}
|
|
line('getenv selected', $envOut);
|
|
line('_ENV count', is_array($_ENV) ? count($_ENV) : 'n/a');
|
|
line('_SERVER selected', [
|
|
'PATH' => $_SERVER['PATH'] ?? null,
|
|
'USER' => $_SERVER['USER'] ?? null,
|
|
'HOME' => $_SERVER['HOME'] ?? null,
|
|
]);
|
|
|
|
section('Self-request capability');
|
|
$selfUrl = null;
|
|
if (!empty($_SERVER['HTTP_HOST'])) {
|
|
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
|
|
$selfUrl = $scheme . '://' . $_SERVER['HTTP_HOST'] . ($_SERVER['REQUEST_URI'] ?? '/');
|
|
}
|
|
line('self url', $selfUrl ?: 'n/a');
|
|
if ($selfUrl && function_exists('file_get_contents')) {
|
|
line('self file_get_contents', with_error_capture(function() use ($selfUrl) {
|
|
$ctx = stream_context_create(['http' => ['timeout' => 2]]);
|
|
$data = @file_get_contents($selfUrl, false, $ctx);
|
|
if ($data === false) return false;
|
|
return 'len=' . strlen($data);
|
|
}));
|
|
}
|
|
|
|
section('Session basics');
|
|
$sessionRes = with_error_capture(function() {
|
|
if (session_status() !== PHP_SESSION_ACTIVE) {
|
|
@session_start();
|
|
}
|
|
$_SESSION['audit_test'] = 'ok';
|
|
return session_id();
|
|
});
|
|
line('session.save_path', ini_get('session.save_path'));
|
|
line('session_start()', $sessionRes);
|
|
line('session file expected', ini_get('session.save_path') . '/sess_' . session_id());
|
|
|
|
section('mail() basics');
|
|
line('function_exists(mail)', function_exists('mail'));
|
|
line('sendmail_path', ini_get('sendmail_path'));
|
|
line('mail() active send test', 'SKIPPED by design');
|
|
|
|
section('.user.ini verification hint');
|
|
$userIniFile = $docRoot . '/.user.ini.audit-test';
|
|
$userIniContent = <<<TXT
|
|
; Rename this file to .user.ini for a live test, then wait for user_ini.cache_ttl
|
|
memory_limit=3072M
|
|
max_execution_time=900
|
|
upload_max_filesize=3072M
|
|
post_max_size=3072M
|
|
auto_prepend_file=
|
|
TXT;
|
|
@file_put_contents($userIniFile, $userIniContent);
|
|
line('Prepared helper file', $userIniFile);
|
|
line('How to test .user.ini', 'Rename .user.ini.audit-test -> .user.ini, wait cache_ttl, reload script, compare values.');
|
|
|
|
section('Assessment');
|
|
|
|
$openBasedir = (string)ini_get('open_basedir');
|
|
$disableFunctions = (string)ini_get('disable_functions');
|
|
$userIni = (string)ini_get('user_ini.filename');
|
|
$allowUrlInclude = (string)ini_get('allow_url_include');
|
|
|
|
if ($openBasedir !== '') {
|
|
add_result('PASS', 'open_basedir is set', $openBasedir);
|
|
} else {
|
|
add_result('FAIL', 'open_basedir is empty');
|
|
}
|
|
|
|
if (!empty($overrideResults['memory_limit']['changed'])) {
|
|
add_result('FAIL', 'memory_limit can be changed via ini_set()', json_encode($overrideResults['memory_limit']));
|
|
} else {
|
|
add_result('PASS', 'memory_limit is not changeable via ini_set()');
|
|
}
|
|
|
|
if (!empty($overrideResults['max_execution_time']['changed'])) {
|
|
add_result('FAIL', 'max_execution_time can be changed via ini_set()', json_encode($overrideResults['max_execution_time']));
|
|
} else {
|
|
add_result('PASS', 'max_execution_time is not changeable via ini_set()');
|
|
}
|
|
|
|
if (!empty($overrideResults['upload_max_filesize']['changed'])) {
|
|
add_result('FAIL', 'upload_max_filesize can be changed via ini_set()', json_encode($overrideResults['upload_max_filesize']));
|
|
} else {
|
|
add_result('PASS', 'upload_max_filesize resisted ini_set()');
|
|
}
|
|
|
|
if (!empty($overrideResults['post_max_size']['changed'])) {
|
|
add_result('FAIL', 'post_max_size can be changed via ini_set()', json_encode($overrideResults['post_max_size']));
|
|
} else {
|
|
add_result('PASS', 'post_max_size resisted ini_set()');
|
|
}
|
|
|
|
if (!empty($overrideResults['open_basedir']['changed'])) {
|
|
add_result('FAIL', 'open_basedir can be changed via ini_set()', json_encode($overrideResults['open_basedir']));
|
|
} else {
|
|
add_result('PASS', 'open_basedir resisted ini_set()');
|
|
}
|
|
|
|
$dangerousAvailable = [];
|
|
foreach (['exec','shell_exec','system','passthru','proc_open','popen'] as $fn) {
|
|
if (function_exists($fn) && !str_contains($disableFunctions, $fn)) {
|
|
$dangerousAvailable[] = $fn;
|
|
}
|
|
}
|
|
if ($dangerousAvailable) {
|
|
add_result('FAIL', 'Command execution functions are available', implode(', ', $dangerousAvailable));
|
|
} else {
|
|
add_result('PASS', 'Command execution functions are blocked');
|
|
}
|
|
|
|
if (extension_loaded('pcntl')) {
|
|
add_result('FAIL', 'pcntl extension is loaded', 'Not recommended for shared hosting web SAPI');
|
|
} else {
|
|
add_result('PASS', 'pcntl extension is not loaded');
|
|
}
|
|
|
|
if ($userIni === '' || strtolower($userIni) === 'none') {
|
|
add_result('PASS', '.user.ini appears disabled');
|
|
} else {
|
|
add_result('WARN', '.user.ini appears enabled', $userIni);
|
|
}
|
|
|
|
if ($allowUrlInclude === '' || $allowUrlInclude === '0') {
|
|
add_result('PASS', 'allow_url_include is off');
|
|
} else {
|
|
add_result('FAIL', 'allow_url_include is on');
|
|
}
|
|
|
|
if (is_link($symlinkTarget)) {
|
|
add_result('WARN', 'Symlink creation works inside allowed path', $symlinkTarget);
|
|
} else {
|
|
add_result('PASS', 'Symlink creation did not work');
|
|
}
|
|
|
|
if (file_exists($hardlinkTarget)) {
|
|
add_result('WARN', 'Hardlink creation works inside allowed path', $hardlinkTarget);
|
|
} else {
|
|
add_result('PASS', 'Hardlink creation did not work');
|
|
}
|
|
|
|
$localhostSensitiveOpen = [];
|
|
foreach (['127.0.0.1:25','127.0.0.1:3306','127.0.0.1:6379','127.0.0.1:7080'] as $k) {
|
|
if (!empty($netResults[$k]['connected'])) {
|
|
$localhostSensitiveOpen[] = $k;
|
|
}
|
|
}
|
|
if ($localhostSensitiveOpen) {
|
|
add_result('WARN', 'Sensitive localhost TCP ports reachable', implode(', ', $localhostSensitiveOpen));
|
|
} else {
|
|
add_result('PASS', 'Sensitive localhost TCP ports not reachable');
|
|
}
|
|
|
|
$reachableUnix = [];
|
|
foreach ($unixResults as $sock => $res) {
|
|
if (!empty($res['connected'])) {
|
|
$reachableUnix[] = $sock;
|
|
}
|
|
}
|
|
if ($reachableUnix) {
|
|
add_result('WARN', 'Sensitive UNIX sockets reachable', implode(', ', $reachableUnix));
|
|
} else {
|
|
add_result('PASS', 'Sensitive UNIX sockets not reachable');
|
|
}
|
|
|
|
section('Score');
|
|
line('PASS', $SCORE['PASS']);
|
|
line('WARN', $SCORE['WARN']);
|
|
line('FAIL', $SCORE['FAIL']);
|
|
|
|
section('Findings');
|
|
foreach ($FINDINGS as [$level, $title, $detail]) {
|
|
echo '[' . $level . '] ' . $title;
|
|
if ($detail !== '') {
|
|
echo ' :: ' . $detail;
|
|
}
|
|
echo PHP_EOL;
|
|
}
|
|
|
|
section('Cleanup');
|
|
$cleanupFiles = [
|
|
$renameTo,
|
|
$copyToSession,
|
|
$src,
|
|
$symlinkTarget,
|
|
$hardlinkTarget,
|
|
$includeFile,
|
|
$userIniFile,
|
|
];
|
|
foreach ($cleanupFiles as $f) {
|
|
if (is_link($f) || file_exists($f)) {
|
|
@unlink($f);
|
|
}
|
|
}
|
|
@rmdir($baseTmp);
|
|
|
|
echo PHP_EOL . "Done." . PHP_EOL;
|