Погружение в unserialize(): больше, чем RCE


Обход аутентификации и внедрение SQL-кода через unserialize() в PHP

Сериализация в PHP через метод unserialize() приводит к появлению уязвимостей, открывающих через RCE (удаленное выполнение кода) доступ для хакеров. Даже при отсутствии RCE существует вероятность обхода системы аутентификации и внедрения SQL-кода.

Обход механизма аутентификации

Уязвимости unserialize() часто используются для обхода проверки безопасности приложения. Есть два способа реализации. Первый заключается в управлении свойствами объекта, чтобы получить контроль доступа. Второй способ  —  манипуляции с типами. Оба метода основаны на том, что конечный пользователь может управлять переданным в функцию объектом.

Управление свойствами объекта

Это один из самых простых и распространенных способов использования уязвимости десериализации для обхода аутентификации.

class User{ public $username = "vickie"; public $type = "Regular User"; # еще PHP код }

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

Так как у конечного пользователя есть право на управление объектом User, он может изменить характеристики объекта и зарегистрироваться как пользователь с правами администратора.

class User{ public $username = "vickie"; public $type = "Admin User"; # еще немного PHP кода } Манипулирование типами переменных

Еще один простой способ взлома, основанный на функции манипуляции типами в PHP. В качестве примера рассмотрим код, используемый для входа в систему под именем администратора:

parse_str($_POST['user_password'], $password_array); $pw = unserialize($password_array[0]); if ($pw->password == "Admin_Password") {login_as_admin();}

Хакер может отправить запрос POST, чтобы войти как админ:

class Password{ public $password = 0; # и еще PHP код } # строка принята как тело POST: print urlencode(serialize(new Password));

Это сработает, так как ответом на (0 == “Admin_Password”) будет true. При сравнении разных типов данных в PHP запускается процесс сведения их к одному типу. Следовательно, “Admin_Password” превратится в 0. И выражение станет эквивалентным (0 == 0).

SQL-инъекция

При определенных условиях уязвимости unserialize() могут быть использованы для внедрения SQL-кода.

Использование POP-цепочек

Предположим, где-то в коде приложение определяет класс Example3 и десериализует нефильтрованный пользовательский ввод из данных POST.

class Example3 { protected $obj; function __construct() { // PHP код... } function __toString() { if (isset($this->obj)) return $this->obj->getValue(); } } // немного PHP кода... $user_data = unserialize($_POST['data']); // еще чуть-чуть PHP кода...

__toString()  —  это магический метод, вызываемый каждый раз, когда класс рассматривается как строка. В таком случае Example3 обрабатывается как строка, и в результате метода getValue() возвращается свойство $obj.

Предположим, в приложении также определен класс SQL_Row_Value с методом getValue(), который выполняет SQL запрос. Запрос получает данные из свойства $_table экземпляра SQL_Row_Value.

class SQL_Row_Value { private $_table; // немного PHP кода... function getValue($id) { $sql = "SELECT * FROM {$this->_table} WHERE id = " . (int)$id; $result = mysql_query($sql, $DBFactory::getConnection()); $row = mysql_fetch_assoc($result); return $row['value'];

Хакер может внедрить SQL-код, используя $obj в Example3: нижеследующий код создаст экземпляр Example3 с $obj, где значение $_table в SQL_Row_Value будет определено как строка “SQL Injection”.

class SQL_Row_Value { private $_table = "SQL Injection"; } class Example3 { protected $obj; function __construct() { $this->obj = new SQL_Row_Value; } } print urlencode(serialize(new Example3));

Всякий раз, когда этот экземпляр обрабатывается как строка, вызывается метод get_Value() в $obj. То есть для SQL_Row_Value этот метод будет выполняться со строкой “SQL Injection”.

Таким образом, хакер реализовал ограниченное внедрение SQL-кода, поскольку он может контролировать строку, переданную в SQL запрос "SELECT * FROM {$this->_table} WHERE id = ". (int)$id;


Перевод статьи Vickie Li: Diving into unserialize(): More than RCE


Поделиться статьей:


Вернуться к статьям

Комментарии

    Ничего не найдено.