Статьи

Анатомия эксплойта: углубленный взгляд на уязвимости Rails, связанные с YAML

yaml_hacked Эксплойты случаются, и в этом месяце сообщества Rails и Ruby не увидели недостатка. От серьезного эксплойта в Rails до немного другой атаки на Rubygems.org никогда не было лучшего времени для повышения безопасности программного обеспечения.

Может быть, вам интересно, почему эти уязвимости возникают в первую очередь, почему они не были пойманы в первую очередь, или, может быть, вы просто хотите узнать специфику этой атаки. Мы начнем с рассмотрения анатомии эксплойта безопасности, а затем углубимся в мрачные детали проблемы YAML.

Почему происходит небезопасный код

Никто не собирается писать небезопасные программы. Эти уязвимости являются ошибками в программном обеспечении, которые могут быть использованы другими. В отличие от обычной ошибки, которая приводит к тому, что ваше программное обеспечение не функционирует должным образом, ошибка, которая открывает дыру в безопасности, может по-прежнему работать нормально для вашей задачи и фактически никогда не выдавать никаких ошибок. Часто это происходит из-за побочных эффектов в вашем коде. Давайте посмотрим на некоторые примеры кода:

Неожиданный ввод

Допустим, по какой-то причине вам нужно удвоить число, поэтому вы пишете такую ​​функцию:

def double(number) return number * 2 end 

Вы проверяете это, и он отлично работает:

 double(10) # => 20 в double(10) # => 20 

Хотя это не небезопасная функция, ее можно использовать не по назначению. Посмотрите, что происходит, когда мы передаем строку в функцию:

 double("foo") # => "foofoo" 

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

На самом деле это не открывает никаких брешей в безопасности, а просто показывает, как программное обеспечение, предназначенное для выполнения какой-либо задачи, может иногда иметь непредвиденные способности.

Реальный пример

Мы рассмотрим все элементы, которые позволили атаке YAML произойти в системе Rails. Вы должны знать, что вся эта информация уже общедоступна и находится в руках «плохих парней». Надеемся, что эта информация поможет вам написать лучший код и принимать более правильные решения в будущем.

Если вы обнаружите уязвимость безопасности в платформе или платформе, сделайте ответственно и сообщите об этом владельцам, прежде чем обнародовать информацию.

Чтобы понять атаку, вам нужно понимать YAML, давайте сделаем быстрый переподготовку.

YAML Refresher

YAML ( язык разметки YAML ) часто используется Rubyists для хранения файлов конфигурации. Самый известный файл yml — это, вероятно, config/database.yml используемый в Rails, и выглядит он так:

 development: adapter: postgresql encoding: utf8 database: example_development pool: 5 host: localhost 

И можно прочитать с помощью YAML::load_file

 require 'yaml' database_config = YAML::load_file('database.yml') puts database_config["development"] # => {"adapter"=>"postgresql", "encoding"=>"utf8", "database"=>"example_development", "pool"=>5, "host"=>"localhost"} 

YAML не только для файлов, это формат сериализации, такой как JSON. Мы можем использовать его для передачи сложных объектов, таких как строки, числа и массивы. Существует даже поддержка для передачи произвольных пользовательских объектов, таких как User или Product . Здесь мы попадаем в беду.

Рубиновые объекты в YAML

YAML позволяет нам непосредственно представлять объекты ruby, лучший способ понять, как это работает, — увидеть его в действии. Давайте посмотрим, как построить простой массив. Вы можете поместить это в .yml файла .yml и прочитать его в:

 --- !ruby/array:Array - jacket - sweater 

Когда вы анализируете это, вы должны получить ["jacket", "sweater"] . Но мы не ограничены простыми объектами Ruby, такими как массивы и строки, мы можем создать любой класс в нашем проекте, например User если мы хотим:

 --- !ruby/hash:User email: [email protected] 

Теперь, когда мы загружаем эту строку в формате YAML в:

 string = "--- !ruby/hash:Usern" + "email: [email protected]" YAML::load(string) => #<User id: 1, email: "[email protected]"> 

Мы получаем объект User. По сути, Ruby берет все атрибуты слева, такие как «электронная почта», и применяет их в качестве значений справа к новому объекту, как если бы это был хеш. Как это:

 user = User.new user["email"] = "[email protected]" 

Это желаемая функциональность для создателей YAML, поскольку она дает разработчикам возможность записывать и читать объекты Ruby на диск, например, объектную базу данных. К сожалению, это часто упускаемая возможность YAML. Кроме того, эта способность имеет непредвиденный побочный эффект, очень похожий на возможность передачи строки в наш метод double() .

Уязвимый объект

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

Чтобы воспользоваться этим эксплойтом, нам нужен класс в нашем коде, который оценивает код либо при создании:

 user = User.new 

или когда мы устанавливаем значения

 user["email"] = "[email protected]" 

Поскольку «email» и «[email protected]» являются значениями, мы можем манипулировать через YAML, который является лучшим местом для поиска.

В этом случае уязвимый класс ActionDispatch::Routing::RouteSet::NamedRouteCollection был найден ActionDispatch::Routing::RouteSet::NamedRouteCollection для использования через @lian и объявлен через Rapid7 . Чтобы понять, как этот класс можно использовать, давайте попробуем запустить произвольный код для класса напрямую.

Сначала мы создаем экземпляр класса:

 unsafe_object = ActionDispatch::Routing::RouteSet::NamedRouteCollection.new 

Тогда сделайте ценность:

 struct = OpenStruct.new(defaults: {}) 

Теперь мы создаем полезную нагрузку foo; eval(puts '=== hello there'.inspect); foo; eval(puts '=== hello there'.inspect); и установите атрибут полезной нагрузки равным значению, как это:

 unsafe_object["foo; eval(puts '=== hello there'.inspect);" ] = struct 

Теперь, когда вы запустите этот код, вы получите ошибку, но перед этой ошибкой будет выполнен любой код, который вы вставили в эту eval() :

 # => "=== hello there" 

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

Эксплойт

Мы знаем, что у нас есть класс, который выполняет произвольный код:

 unsafe_object = ActionDispatch::Routing::RouteSet::NamedRouteCollection.new struct = OpenStruct.new(defaults: {}) unsafe_object["foo; eval(puts '=== hello there'.inspect);"] = struct # => "=== hello there" 

И мы знаем, что можем создавать такие объекты, используя YAML:

 --- !ruby/hash:ActionDispatch::Routing::RouteSet::NamedRouteCollection 'foo; eval(eval(puts '=== hello there'.inspect);': !ruby/object:OpenStruct table: :defaults: {} 

Даже с обоими этими элементами наше приложение на Rails все еще безопасно, если только пользователям не разрешено отправлять приложению произвольный загружаемый YAML. Как вы, вероятно, догадались, была ошибка, которая позволяла злоумышленнику использовать запрос XML для внедрения YAML в приложение Rails. Когда вы соединяете эти три элемента вместе, вы получаете систему, которая может быть полностью захвачена злоумышленником. Если кто-то может запустить произвольный код на вашем сервере, вы больше не являетесь владельцем этого сервера.

Задний 20/20

Дырки в парсерах Rails XML и JSON для разных уязвимых версий исправлены, и некоторые спрашивают, почему они не были обнаружены и исправлены ранее. Ответ прост: безопасность сложна. Эти проблемы очевидны только в ретроспективе. Rails и Ruby не менее безопасны, чем другие фреймворки и языки. Уязвимости безопасности являются ошибками по своей сути, и их очень трудно обнаружить. Почти наверняка где-то на вашем ноутбуке / телефоне / сервере / гаражном устройстве открываются небезопасные программы — они просто еще не обнаружены.

Надеюсь, вы уже поняли, насколько сложно обнаружить эти уязвимости. Мы можем вооружиться знаниями и здоровым недоверием к пользовательской информации.

Знание — сила

Чем больше людей понимают, как YAML можно использовать против системы, тем легче будет обнаружить дыру в безопасности до ее запуска в эксплуатацию. При обнаружении уязвимостей важно, чтобы разработчики этого программного обеспечения понимали основные причины и помогали распространять знания среди других. Я не исследователь безопасности и не специалист, а просто парень, который писал код на Rails несколько лет. Пока я не начал исследовать эту атаку, я не знал почему, но теперь я никогда не забуду.

Никогда не доверяйте своим пользователям

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

Будьте в курсе

Подпишитесь на списки рассылки или группы, которые публикуют объявления о безопасности для основных компонентов программного обеспечения, которые вы используете, таких как группа безопасности Rails . Когда объявление выходит, обновите немедленно, даже если угроза мала. Надеюсь, вы видели, как одно неожиданное использование программного обеспечения может привести к другому. Компания, в которой я работаю, Heroku, сочла угрозу от этой проблемы настолько серьезной, что мы уведомили людей, использующих уязвимый код.

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

резюмировать

Возможно, вы не являетесь исследователем безопасности, но, надеюсь, вы узнали кое-что не только об этой YAML-атаке, но и о природе эксплойта безопасности. Вы научились не доверять пользовательскому вводу и узнали, что знание — это сила. Так что поделитесь своими знаниями, отправив эту статью другому разработчику, они могут это оценить.