1. Обзор
В этом руководстве мы рассмотрим, как злоумышленник может использовать десериализацию в коде Java для взлома системы.
Мы начнем с рассмотрения некоторых различных подходов, которые злоумышленник может использовать для эксплуатации системы. Затем мы рассмотрим последствия успешной атаки. Наконец, мы рассмотрим некоторые рекомендации, которые помогут избежать подобных атак.
2. Уязвимости десериализации
Java широко использует десериализацию для создания объектов из входных источников.
Эти источники ввода представляют собой потоки байтов и имеют различные форматы (некоторые стандартные формы включают JSON и XML). Легальные функциональные возможности системы или связь с надежными источниками по сети используют десериализацию. Однако ненадежные или вредоносные потоки байтов могут использовать уязвимый код десериализации.
Наша предыдущая статья о сериализации Java более подробно описывает, как работают сериализация и десериализация.
2.1. Вектор атаки
Давайте обсудим, как злоумышленник может использовать десериализацию для взлома системы.
Чтобы класс можно было сериализовать, он должен соответствовать интерфейсу Serializable .
Классы, реализующие Serializable
, используют методы readObject
и writeObject.
Эти методы десериализуют и сериализуют экземпляры объектов класса соответственно.
Типичная реализация этого может выглядеть так:
public class Thing implements Serializable {
private static final long serialVersionUID = 0L;
// Class fields
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject();
// Custom attribute setting
}
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
// Custom attribute getting
}
}
Классы становятся уязвимыми, когда они имеют общие или слабо определенные поля и используют отражение для установки атрибутов в этих полях :
public class BadThing implements Serializable {
private static final long serialVersionUID = 0L;
Object looselyDefinedThing;
String methodName;
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject();
try {
Method method = looselyDefinedThing.getClass().getMethod(methodName);
method.invoke(looselyDefinedThing);
} catch (Exception e) {
// handle error...
}
}
// ...
}
Давайте разберем вышесказанное, чтобы увидеть, что происходит.
Во- первых, в нашем классе BadThing
есть поле слабоопределенная вещь
типа Object.
Это расплывчато и позволяет злоумышленнику сделать это поле любым типом, доступным в пути к классам.
Далее, что делает этот класс уязвимым, так это то, что метод readObject
содержит настраиваемый код, который вызывает метод на уязвимостиDefinedThing
. Метод, который мы хотим вызвать, использует поле methodName
(которое также может контролироваться злоумышленником) посредством отражения.
Приведенный выше код эквивалентен следующему при выполнении, если класс MyCustomAttackObject
находится в системном пути к классам:
BadThing badThing = new BadThing();
badThing.looselyDefinedThing = new MyCustomAttackObject();
badThing.methodName = "methodThatTriggersAttack";
Method method = looselyDefinedThing.getClass().getMethod(methodName);
method.invoke(methodName);
public class MyCustomAttackObject implements Serializable {
public static void methodThatTriggersAttack() {
try {
Runtime.getRuntime().exec("echo \"Oh, no! I've been hacked\"");
} catch (IOException e) {
// handle error...
}
}
}
Используя класс MyCustomAttackObject
, злоумышленник смог выполнить команду на хост-компьютере.
Эта конкретная команда безвредна. Однако, если бы этот метод мог принимать пользовательские команды, возможности того, чего может достичь злоумышленник, безграничны.
Вопрос, который все еще остается в силе: «Зачем вообще кому-то нужен такой класс в пути к классам?».
Классы, которые позволяют злоумышленнику выполнять вредоносный код, широко распространены в библиотеках с открытым исходным кодом и сторонних библиотеках, которые используются многими платформами и программным обеспечением. Они часто не так просты, как в приведенном выше примере, но включают использование нескольких классов и отражение, чтобы иметь возможность выполнять команды подобного рода.
Использование нескольких классов таким образом часто называют цепочкой гаджетов. Инструмент с открытым исходным кодом ysoserial поддерживает активный список цепочек гаджетов, которые можно использовать в атаке.
2.2. Подразумеваемое
Теперь, когда мы знаем, как злоумышленник может получить доступ к удаленному выполнению команд, давайте обсудим некоторые последствия того, чего злоумышленник может добиться в нашей системе.
В зависимости от уровня доступа, который имеет пользователь, работающий с JVM, злоумышленник может уже иметь повышенные привилегии на машине, что позволит ему получить доступ к большинству файлов в системе и похитить информацию.
Некоторые эксплойты десериализации позволяют злоумышленнику выполнять пользовательский код Java, что может привести к атакам типа «отказ в обслуживании», краже пользовательского сеанса или несанкционированному доступу к ресурсам.
Поскольку каждая уязвимость десериализации отличается, а каждая система настроена по-разному, то, чего может добиться злоумышленник, сильно различается. По этой причине базы данных уязвимостей считают уязвимости десериализации высоким риском.
3. Передовой опыт профилактики
Теперь, когда мы рассмотрели, как наша система может быть взломана, мы коснемся некоторых передовых методов, которым можно следовать, чтобы предотвратить этот тип атаки и ограничить масштаб потенциальных эксплойтов.
Обратите внимание, что в предотвращении эксплойтов не существует серебряной пули, и этот раздел не является исчерпывающим списком всех превентивных мер:
- Мы должны поддерживать библиотеки с открытым исходным кодом в актуальном состоянии. Приоритизируйте обновление до последней версии библиотек, если они доступны.
- Активно проверять базы данных уязвимостей, такие как Национальная база данных уязвимостей или CVE Mitre (и это лишь некоторые из них), на наличие вновь объявленных уязвимостей и следить за тем, чтобы нас не подвергали риску.
- Проверьте источник входного потока байтов для десериализации (используйте безопасные соединения и проверьте пользователя и т. д.)
- Если ввод поступил из поля ввода пользователя, обязательно проверьте эти поля и авторизуйте пользователя перед десериализацией.
- Следуйте памятке owasp по десериализации при создании пользовательского кода десериализации .
- Ограничьте доступ JVM к хост-компьютеру, чтобы уменьшить возможности злоумышленника, если он сможет использовать нашу систему.
4. Вывод
В этой статье мы рассмотрели, как злоумышленник может использовать десериализацию для эксплуатации уязвимой системы. Кроме того, мы рассмотрели некоторые методы поддержания надлежащей гигиены безопасности в системе Java.
Как всегда, исходный код доступен на GitHub .