Русский
Русский
English
Статистика
Реклама

Injection

Web Security by Bugbounty

06.11.2020 14:12:14 | Автор: admin

Александр Колесников (вирусный аналитик в международной компании) приглашает на мастер-класс Основы технологии, необходимые для понимания уязвимостеи. Классификация OWASP TOP 10, который пройдёт в рамках профессионального курса . А также Александр поделился статьёй для начинающих bug hunter-ов, где рассматривает TOP 10 Уязвимостей 2020 года, которые были найдены платформой HackerOne.

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

TOP 10 уязвимостей by HackerOne

Информацию по топу уязвимостей можно найти здесь. Приведу ниже статистику по выплатам для каждой из уязвимостей:

Напрашивается сравнение с очень популярным списком OWASP TOP 10. Но надо учитывать, что последний раз обновление в OWASP TOP 10 вносились в 2017 году. На данный момент ведется сбор информации для обновления списка. Итак, проведем сравнительный анализ данных рейтингов:

В списке OWASP представлены множества уязвимостей, тогда как в списке HackerOne содержатся конкретные уязвимости. Объединим все уязвимости из списка HackerOne в типы:

  • Injection

  • Broken Authentication

  • Sensitive Data Exposure

  • Security Misconfiguration

Получается, что для создания подходящей лаборатории для исследований достаточно найти 3 уязвимых приложения.

Injection

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

В качестве тестового рассмотрим приложение, которое было использовано на соревновании Real World CTF. Приложение предоставляет пользователю информацию о книгах.

Попробуем выяснить, какая структура у данного приложения. Отправим любой HTTP запрос с помощью BurpSuite Community, получим следующее:

Форма логина и страница admin представляет собой одну и ту же страницу Login.

Стандартные тесты на типичные уязвимости для этой формы не принесли результата, также не подошли стандартные пароли. Вероятно, это не является вектором для атаки. Обратимся к подсказке, которую оставили создатели уязвимого приложения. Подсказка заключается в исходном коде приложения. Скачать его можно по адресу source.zip. Таким образом, у нас есть полностью работоспособное приложение, которое можно использовать для локального исследования и поиска уязвимостей/недочетов, которые допущены при его написании.

Нас интересуют только файлы, которые содержат код:

После анализа исходного кода становится ясно, что приложение использует фреймворк для создания веб-приложений Flask. Известно, что этот фреймворк может запускать приложение в 2х режимах - debug и release. В debug версии стандартный порт - 5000. Проверим, используется ли этот порт на сервере:

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

Файл, который представляет наибольший интерес:

.\views\user.py

Уязвимость находится в отрезке кода, который представлен выше на скриншоте. Проблема данного кода заключается в том, что декоратор @login_required указан в неверной последовательности, из-за чего его использование бессмысленно. Любой пользователь приложения может использовать код, который вызывается через обращение к /admin/system/change_name/. Так же есть кусок кода, который так же может быть интересен:

В исходнике используется часть Lua кода для работы с Redis. Данные инициализируются прямо из токена, который отправляет на сервер пользователь. Атака на приложение может быть проведена через Redis. Данные из токена в дальнейшем будут отправлены на обработку модулю python pickle. Это можно использовать для инжекта кода.

Security Misconfiguration

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

Снова будем использовать задание из соревнования Real World CTF. Приложение представляет собой простой загрузчик файлов и может быть использовано для загрузки картинок из любого источника:

Проверим интерфейс на возможность чтения локальных файлов, к примеру, file:///etc/passwd. Этот прием нам поможет выяснить есть ли уязвимость из заголовка в этом задании. Результат можно увидеть ниже:

Задание подразумевает возможность заставить сервер выполнять команды, которые мы ему сообщаем, благодаря добытой с помощью эксплуатации уязвимости информации. Поэтому следующим шагом нам будет полезно узнать, как именно приложение запущено на сервере. С помощью команды file:///proc/self/cmdline можно получить строку запуска приложения:

Приложение работает с использованием uwsgi-сервера и может использовать соответствующий протокол для трансляции данных от сервера к приложению и наоборот. Сервер работает через сокет 8000. Рабочая директория приложения - /usr/src/rwctf. Для завершения атаки достаточно научиться создавать команды для uwsgi, с помощью которых можно будет управлять сервером.

Broken Authentication, Sensitive Data Exposure

Broken Authentication - уязвимость, которая может быть использована для доступа к ресурсам приложения в обход системы разграничения доступа. Sensitive Data Exposure - уязвимость, которая может быть использована для доступа к чувствительным данным приложения. Это могут быть конфигурации приложения, логины и пароли от сторонних сервисов и т.д.

Следующее приложение, которое будет изучено, собрало максимальное количество уязвимостей из искомого списка. Данное Приложение было использовано на соревновании 35с3 CTF. Оно предоставляет минимальный интерфейс для регистрации пользователей и получения доступа к закрытой части. Это выглядит следующим образом:

Поля ввода не принимают ничего, кроме корректной последовательности данных. Попробуем проверить, насколько безошибочно настроен сервер для обработки данных. Запустим инструмент dirbuster, чтобы найти список директорий, которые относятся к приложению. В итоге была найдена директория uploads, которая возвращает HTTP код 403. Модифицируем путь c помощью специальных символов файловой системы. Для этого просто добавим символ перехода в директорию на уровень выше: /uploads../:

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

Вместо вывода

Содержимое данной статьи может быть использовано в качестве лабораторной работы для тех, кто только начинает путь поиска уязвимостей в веб-приложениях. С их помощью можно получить базовые практические навыки тестирования веб-приложений и приобрести первый самостоятельный опыт в данной сфере.

Disclamer: Все использованные приложения принадлежат их авторам.


Информацию по топу уязвимостей можно найти здесь. Интересно развиваться в данном направлении? Узнайте больше о профессиональной программе Безопасность веб-приложений, приходите на День Открытых Дверей и запишитесь на бесплатный демо-урок Основы технологии, необходимые для понимания уязвимостеи. Классификация OWASP TOP 10.

Подробнее..

Recovery mode Типы, где их не ждали

12.11.2020 08:22:24 | Автор: admin

Давайте представим себе реализацию модуля Scaffold, который генерирует структуру с предопределенными пользовательскими полями и инжектит ее в вызываемый модуль при помощи use Scaffold. При вызове use Scaffold, fields: foo: [custom_type()], ... мы хотим реализовать правильный тип в Consumer модуле (common_field в примере ниже определен в Scaffold или еще где-нибудь извне).


@type t :: %Consumer{  common_field: [atom()],  foo: [custom_type()],  ...}

Было бы круто, если бы мы могли точно сгенерировать тип Consumer.t() для дальнейшего использования и создать соответствующую документацию для пользователей нашего нового модуля.


Lighthouse in French Catalonia


Пример посложнее будет выглядеть так:


defmodule Scaffold do  defmacro __using__(opts) do    quote do      @fields unquote(opts[:fields])      @type t :: %__MODULE__{        version: atom()        # magic      }      defstruct @fields    end  endenddefmodule Consumer do  use Scaffold, fields: [foo: integer(), bar: binary()]end

и, после компиляции:


defmodule Consumer do  @type t :: %Consumer{    version: atom(),    foo: integer(),    bar: binary()  }  defstruct ~w|version foo bar|aend

Выглядит несложно, да?


Наивный подход


Давайте начнем с анализа того, что за AST мы получим в Scaffold.__using__/1.


  defmacro __using__(opts) do    IO.inspect(opts)  end# [fields: [foo: {:integer, [line: 2], []},#            bar: {:binary, [line: 2], []}]]

Отлично. Выглядит так, как будто мы в шаге от успеха.


  quote do    custom_types = unquote(opts[:fields])    ...  end# == Compilation error in file lib/consumer.ex ==#  ** (CompileError) lib/consumer.ex:2: undefined function integer/0

Бамс! Типыэто чего-то особенного, как говорят в районе Привоза; мы не можем просто взять и достать их из AST где попало. Может быть, unquote по месту сработает?


      @type t :: %__MODULE__{              unquote_splicing([{:version, atom()} | opts[:fields]])            }# == Compilation error in file lib/scaffold.ex ==#  ** (CompileError) lib/scaffold.ex:11: undefined function atom/0

Как бы не так. Типыэто утомительно; спросите любого, кто зарабатывает на жизнь хаскелем (и это еще в хаскеле типы курильщика; настоящие зависимые типы в сто раз полезнее, но еще в двести раз сложнее).


Ладно, кажется, нам нужно собрать все это богатство в AST и инжектнуть его целиком, а не по частям, чтобы компилятор увидел сразу правильное объявление.


Построение типа в AST


Я опущу тут пересказ нескольких часов моих метаний, мучений, и тычков пальцем в небо. Все знают, что я пишу код в основном наугад, ожидая, что вдруг какая-нибудь комбинация этих строк скомпилируется и заработает. В общем, сложности тут с контекстом. Мы должны пропихнуть полученные определения полей в неизменном виде напрямую в макрос, объявляющий тип, ни разу не попытавшись это AST анквотнуть (потому что в момент unquote типы наподобие binary() будут немедленно приняты за обыкновенную функцию и убиты из базуки вызваны компилятором напрямую, приводя к CompileError.


Кроме того, мы не можем использовать обычные функции внутри quote do, потому что все содержимое блока, переданного в quote, уже само по себеAST.


quote do  Enum.map([:foo, :bar], & &1)end# {#   {:., [], [{:__aliases__, [alias: false], [:Enum]}, :map]}, [],#     [[:foo, :bar], {:&, [], [{:&, [], [1]}]}]}

Видите? Вместо вызова функции, мы получили ее препарированное AST, все эти Enum, :map, и прочий маловнятный мусор. Иными словами, нам придется создать AST определения типа вне блока quote и потом просто анквотнуть внутри него. Давайте попробуем.


Чуть менее наивная попытка


Итак, нам надо инжектнуть AST как AST, не пытаясь его анквотнуть. Звучит устрашающе? Вовсе нет, отнюдь.


defmacro __using__(opts) do  fields = opts[:fields]  keys = Keyword.keys(fields)  type = ???  quote location: :keep do    @type t :: unquote(type)    defstruct unquote(keys)  endend

Все, что нам нужно сделать сейчас, это произвести надлежащий AST, все остальное в порядке. Ну, пусть ruby сделает это за нас!


iex|1  quote do...|1    %Foo{version: atom(), foo: binary()}...|1  end#{:%, [],#   [#     {:__aliases__, [alias: false], [:Foo]},#     {:%{}, [], [version: {:atom, [], []}, foo: {:binary, [], []}]}#   ]}

А нельзя ли попроще?


iex|2  quote do...|2    %{__struct__: Foo, version: atom(), foo: binary()}...|2  end# {:%{}, [],#   [#     __struct__: {:__aliases__, [alias: false], [:Foo]},#     version: {:atom, [], []},#     foo: {:binary, [], []}#   ]}

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


Почти работающее решение


defmacro __using__(opts) do  fields = opts[:fields]  keys = Keyword.keys(fields)  type =    {:%{}, [],      [        {:__struct__, {:__MODULE__, [], ruby}},        {:version, {:atom, [], []}}        | fields      ]}  quote location: :keep do    @type t :: unquote(type)    defstruct unquote(keys)  endend

или, если нет цели пробросить типы из собственно Scaffold, даже проще (как мне вот тут подсказали: Qqwy here). Осторожно, оно не будет работать с проброшенными типами, version: atom() за пределами блока quote выбросит исключение.


defmacro __using__(opts) do  fields = opts[:fields]  keys = Keyword.keys(fields)  fields_with_struct_name = [__struct__: __CALLER__.module] ++ fields  quote location: :keep do    @type t :: %{unquote_splicing(fields_with_struct)}    defstruct unquote(keys)  endend

Вот что получится в результате генерации документации для целевого модуля (mix docs):


Screenshot of type definition


Примечание: трюк с фрагментом AST


Но что, если у нас уже есть сложный блок AST внутри нашего __using__/1 макроса, который использует значения в кавычках? Переписать тонну кода, чтобы в результате запутаться в бесконечной череде вызовов unquote изнутри quote? Это просто даже не всегда возможно, если мы хотим иметь доступ ко всему, что объявлено внутри целевого модуля. На наше счастье, существует способ попроще.


NB для краткости я покажу простое решение для объявления всех пользовательских полей, имеющих тип atom(), которое тривиально расширяеься до принятия любых типов из входных параметров, включая внешние, такие как GenServer.on_start() и ему подобные. Эту часть я оставлю для энтузиастов в виде домашнего задания.

Итак, нам надо сгенерировать тип внутри блока quote do, потому что мы не можем передавать туда-сюда atom() (оно взовется с CompileError, как я показал выше). Хначит, что-нибудь типа такого:


keys = Keyword.keys(fields)type =  {:%{}, [],    [      {:__struct__, {:__MODULE__, [], ruby}},      {:version, {:atom, [], []}}      | Enum.zip(keys, Stream.cycle([{:atom, [], []}]))    ]}

Это все хорошо, но как теперь добавить этот АСТ в декларацию @type? На помощь приходит очень удобная функция эликсира под названием Quoted Fragment, специально добавленный в язык ради генерации кода во время компиляциию Например:


defmodule Squares do  Enum.each(1..42, fn i ->    def unquote(:"squared_#{i}")(),      do: unquote(i) * unquote(i)  end)endSquares.squared_5# 25

Quoted Fragments автоматически распознаются компилятором внутри блоков quote, с напрямую переданным контекстом (bind_quoted:). Проще простого.


defmacro __using__(opts) do  keys = Keyword.keys(opts[:fields])  quote location: :keep, bind_quoted: [keys: keys] do    type =      {:%{}, [],        [          {:__struct__, {:__MODULE__, [], ruby}},          {:version, {:atom, [], []}}          | Enum.zip(keys, Stream.cycle([{:atom, [], []}]))        ]}    #              @type t :: unquote(type)    defstruct keys  endend

Одинокий вызов unquote/1 тут разрешен, потому что bind_quoted: был напрямую указан как первый аргумент в вызове quote/2.




Удачного внедрения!

Подробнее..
Категории: Open source , Elixir/phoenix , Erlang/otp , Injection , Macros , Macro

Recovery mode Типы в рантайме глубже в крольчью нору

19.11.2020 10:11:44 | Автор: admin

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


Все, что предложено по ссылке, будет работать для явных определений типа по месту использования, наподобие use Foo, var: type(). К сожалению, такой подход обречен, если мы хотим определить типы где-нибудь в другом месте: рядом в коде при помощи атрибутов модуля, или, там, в конфиге. Например, для определения структуры мы можем захотеть написать что-то типа такого:


# @fields [foo: 42]# defstruct @fields@definition var: atom()use Foo, @definition

Lighthouse in French Catalonia


Код выше не то, что не обработает тип так, как нам хочетсяон не соберется вовсе, потому что @definition var: atom() выбросит исключение ** (CompileError) undefined function atom/0.


Наивный подход


Один из моих самых любимых профессиональных афоризмов недели кодирования могут сэкономить вам часы планирования (обычно приписывается @tsilb, но пользователь был заблокирован супердемократичным твиттером, поэтому я не уверен.) Мне она так нравится, что я повторяю ее как мантру на каждом втором совещании, но, как это всегда бывает с незыблемыми жизненными принципами, часто не следую провозглашаемому сам.


Итак, я начал с того, что сделал две разных реализации __using__/1: одну, которая принимает список (и ожидает увидеть в нем пары field type()), и другую принимающую все, что угодно, ожидая встретить в аргументах либо квотированные типы, либо триплы {Module, :type, [params]}. Я использовал сигил ~q||, который был услужливо имплементирован мной же, в одном из стародавних игрушечных проектов, во времена, когда я учился работать с макросами и AST. Он позволяет вместо quote/1 писать лаконичнее: foo: ~q|atom()|. Там внутри я руками строил список, который потом передавался в первую функцию, принимающую списки. Весь этот код был настоящим кошмаром. Я сомневаюсь, что видел что-то более невнятное за всю свою карьеру, несмотря на то, что я чувствую себя абсолютно комфортно с регулярными выражениями, они мне нравятся, и я их часто использую. Однажды я выиграл спор на воспроизведение регулярного выражения для электронной почты максимально близко к оригиналу, но этот код, всего-то передававший туда-сюда старый добрый простой эрланговский тип оказался в пять раз запутаннее и как-то неаккуратнее, что ли.


Все это выглядело безумием. Мне на подсознательном уровне кажется, что работа с нативными типами erlang во время выполненияне должна нуждаться в тонне переусложненного избыточного кода, который весь выглядел как заклинание, которое может вызвать духов Кобола. Поэтому я заварил себе кофе покрепче и начал думать вместо того, чтобы писать код, который никто никогда не сможет понять позже (включая меня самого).


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


Tyyppi


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


В ядре эликсира присутствует незадокументированный модуль Code.Typespec, который существенно облегчил мне жизнь. Я начал с очень простого подхода: с проверки всех возможных термов по всем возможным типам. Я просто загрузил все типы, доступные в моей текущей сессии, и дописывал новые обработчики по мере того, как рекурсивный анализ типов падал глубже по рекурсии. Честно говоря, это было скорее скучно, чем весело. Зато оно привело меня к первой полезной части этой библиотекифункции Tyyppi.of?/2, которая принимает тип и терм, а возвращает логическое значение да/нет в зависимости от того, принадлежит ли терм указанному типу.


iex|tyyppi|1  Tyyppi.of? GenServer.on_start(), {:ok, self()}# trueiex|tyyppi|2  Tyyppi.of? GenServer.on_start(), :ok# false

Мне нужно было какое-то внутреннее представление для типов, поэтому я решил хранить все в виде структуры с именем Tyyppi.T. Так у Tyyppi.of?/2 появился брат-близнец Tyyppi.of_type?/2.


iex|tyyppi|3  type = Tyyppi.parse(GenServer.on_start)iex|tyyppi|4  Tyyppi.of_type? type, {:ok, self()}# true

Единственный нюанс, связанный с этим подходом, заключается в том, что мне нужно загрузить и сохранить все типы, доступные в системе, и эта информация не будет доступна в релизах. На данный момент я прекрасно справляюсь с хранением всего этого в обычном файле при помощи :erlang.term_to_binary/1, который связывается с релизом и загружается через обычный специализированный Config.Provider.


Структуры


Теперь я был полностью вооружен, чтобы вернуться к своей первоначальной задаче: создать удобный способ объявления типизированной структуры. Со всем этим багажом на борту, это было легко. Я решил ограничить само объявление структуры явным встроенным литералом, содержащим пары key: type(). Также я реализовал для него Access, с проверкой типов при upserts. Имея все это под рукой, я решил позаимствовать еще пару идей у Ecto.Changeset и добавил перегружаемые функции cast_field/1 и validate/1.


Теперь мы можем объявить структуру, которая разрешала бы апсерты, тогда и только тогда, когда типы всех значений верны, (и когда проходит пользовательская проверка, если была задана).


defmodule MyStruct do  import Kernel, except: [defstruct: 1]  import Tyyppi.Struct, only: [defstruct: 1]  @typedoc "The user type defined before `defstruct/1` declaration"  @type my_type :: :ok | {:error, term()}  @defaults foo: :default,            bar: :erlang.list_to_pid('<0.0.0>'),            baz: {:error, :reason}  defstruct foo: atom(), bar: GenServer.on_start(), baz: my_type()  def cast_foo(atom) when is_atom(atom), do: atom  def cast_foo(binary) when is_binary(binary),    do: String.to_atom(binary)  def validate(%{foo: :default} = my_struct), do: {:ok, my_struct}  def validate(%{foo: foo} = my_struct), do: {:error, {:foo, foo}end

Я понятия не имею, какова практическая ценность этой библиотеки в продакшене (вру, знаю я: никакая), но она, безусловно, может быть отличным помощником во время разработки, позволяя сузить круг поиска и вычленить странные ошибки, связанные с динамической природой типов в Elixir, особенно при работе с внешними источниками.


Весь код библиотеки доступен, как всегда, на гитхабе.




Удачного рантаймтайпинга!

Подробнее..
Категории: Open source , Elixir/phoenix , Erlang/otp , Injection , Macros , Macro

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru