Тестирование безопасности клиент-серверного API

в 16:13, , рубрики: Блог компании «Digital Security»
2 года назад я выступал на конференции CodeFest с темой «Пентест на стероидах. Автоматизируем процесс» и делал пересказ выступления в качестве статьи. В этом году я с большим удовольствием снова принял участие в конференции и выступил с темой «BlackBox тестирование безопасности клиент-серверного API» и, видимо уже в качестве традиции, также делаю пересказ выступления.

Тестирование безопасности клиент серверного API
Уязвимости в API встречаются. Правда.

О чем разговор?

Разговор про API, но API бывает разным — API операционной системы, сайта, дектопной программы, да хоть чего. Вкратце — API это обращение к методам чего-либо (например в случае ОС — запись в файл) через определенный метод. И запуск какого-нибудь файла с т.ч. зрения разработки произойдет схожим образом (тоже через API метод ОС), хотя результат выполнения команды совершенно разный. В веб-технологиях многие тоже реализуют API к своим проектам, и если на пальцах: можно отправить сообщение в соц. сети зайдя на сайт, а можно — сформировав специальный HTTP запрос. И при реализации подобного альтернативного использования функционала допускаются (как и везде) ошибки в безопасности. Статья как раз о специфичных ошибках при реализации подобного функционала в веб-проектах.

От интерфейса к API методам

Разберем уязвимость в API на youtube, которая не получила широкой огласки. Youtube запрещает из интерфейса использовать спец. символы типа < и > (которые нужны для html тэгов и проведения атаки XSS) в названии роликов. Но! Если найти API и попробовать изменить название этого же видео, то все пройдет успешно. Как итог: нет, xss не выполнилась на странице youtube. Но выполнилась в письме на gmail (когда приходит письмо с роликом), что еще критичнее.

Выводы? Логично — проблема в разной работе методов. А вот почему она случилась — это более интересный вопрос. Здесь бы я выделил следующее:

  • Практически все большие проекты используют ООП и паттерны подобные MVC. И реализации API должны были просто отнаследоваться от метода в интерфейсе и забрать все его ограничения. Значит или нет подобного шаблона и метод писался с нуля (бред? кто знает) или кастомный «хак» в работе интерфейса;
  • Кастомный хак? Проблема разработчика, конечно. Но еще и тимлида, нужно постоянно доносить до разработчиков, где и что нужно реализовывать. Т.е. четко для всех разобрать извечный тред: где в процессе разработки мы валидируем данные (в модели, сервисе, контроллере, где-то еще). Это последствия проблемы применения сложных паттернов при разработке, которые тимлид то понимает нормально, а разработчики не дотягивают и часть валидаций в проекте в одном месте, часть — в другом. Для API корректно отнаследовались, но получили уязвимость.

Что для тестировщиков?

  1. Нужно вообще забыть всё, что мы знали об интерфейсе (его ограничениях) и тестировать проект с нуля;
  2. Проверять подмену параметров, яркий пример со скрином про FaceBook — из интерфейса нельзя было подменить id отправителя, а вот из API — можно;
  3. Проверять «стандартные» атаки, типа sqli/xss;
  4. Если есть автотесты — то это круто. Можно заменить стандартный пейлоад типа testValue1 на различные спецсимволы типа ', ", >, < и матчить их (ищем XSS).

Сжатие

Часто API начинают разрабатывать для применения для мобильных устройств. И, при реализации, добавляют различные сжатия перед отправкой и разжатие после принятия. И есть старая, бородатая атака (ей ложили различные файлообменники) как ZIP бомбы. Вот вопрос: какого размера при распаковке может достичь архив в 42 кб? 4,5 петабайта. Скачать здесь, и тут. Суть простая — создается файл забитый нулями и сжимается. Поэтому сжатие, точнее распаковка — опасное дело, будьте внимательны.

Зло JSON

Иногда API предоставляется не только для какого-то конечного юзера, а порой и для пересылки данных внутри проекта. Часто это встречается на больших, крупных сайтах с различными доменами. И как-то надо взаимодействовать на стороне клиента между доменами, и тут на помощь приходит JSONP. Это такой JSON с нужными на домене 1, который оборачивается в callback. При обращении на домен 1 юзер отправит свои куки, и можно проверить, авторизован ли он и выдать нужные данные. На втором домене вставляется подобный JS

<script type="application/javascript"
        src="http://server1.example.com/api/getUserInfo?jsonp=parseResponse">
</script>

с уже определенной функцией parseResponse. Но суть в том, что злоумышленник может также вставить на своем домене этот скрипт, определить свой callback и, если там sensetive данные — как-то их использовать. Прекрасный пример использования подобной уязвимости продемонстрирован в статье «Сражаясь с анонимностью».

Криптография

Говоря про API сразу на им приходит подписывание запросов. Так как доступ к API часто выдается различным пользователям нужна схема их идентификации. И чаще всего используют следующую схему: каждому выдается свой API ключ, которым разработчик подписывает свои запросы, как-то так:

sign = sha*(... + DATA + ...)

Data — отправляемые данные, вместо многоточия API ключ.
И вопрос, где ставить ключ, слева или справа? Только справа (мы говорим про подписывание запросов именно с применением подобной, очень популярной схемы). Почему? Существует атака на расширение строки. Давайте подробнее.
Представим, что у нас есть данные A=1&B=2&C=3, подпись от них 07ce36c769ae130708258fb5dfa3d37ca5a67514, подпись идет по ошибочной схеме sign=sha1(KEY+DATA).
А теперь ситуация: кто-то перехватил буквально один запрос от клиента к серверу и теперь хочет поменять данные в запросе, но ему потребуется новая подпись, а ключ для подписи не передается (оно и логично). Что он знает? Оригинальные данные и их подпись. Вкратце: есть техническая возможность создать расширить строку (дописать свои данные) и сделать новую подпись (хэш), не зная N байт в начале. На практике новые данные с отступом N байт при хэшировании в начале выглядят так: A=1&B=2&C=3x80x00x00…x02&C=4 . где

  • A=1&B=2&C=3 — изначальные данные
  • x80x00x00…x02 — спец. байты для «отступа» (ключ для подписи) при хэшировании
  • &c=4 — наши новые данные

Тестирование безопасности клиент серверного API
При отправке одного и того же параметра например PHP — возьмет второй. Как раз то, что и нужно для атаки. Так как на первый мы не можем повлиять

Зло победило: можно подписывать запросы, менять параметры не зная ключа для подписи. А вот выдержка из документации по работе с API VK и Mail.RU

Vkontakte: sig = md5(name1=value1name2=value2api_secret) 
Mail.RU sig = md5(uid + params + private_key)

Как видим — ключ справа.

Я обмолвился про кражу всего одного запроса. На практике это бывает чуть проще, чем кажется (история #1, статья)

Небезопасный XML

Думаю каждый встречался с XML, ничем не примечательный тип данных

<recipe name="хлеб" preptime="5" cooktime="180">
  <title>Простой хлеб</title>
  <composition>
    <ingredient amount="3" unit="стакан">Мука</ingredient>
  </composition>
...
</recipe>

Но уже менее людей встречались с сущностями в XML

DTD Example:

<!ENTITY writer "Donald Duck."> 
<!ENTITY copyright "Copyright W3Schools."> 

XML example:

<author>&writer;&copyright;</author> 

Которые позволяют что-то определить, а потом повторно использовать. На примере выше — просто строки.
А еще меньше людей сталкивались с внешними XML сущностями…

<!DOCTYPE foo [ 
<!ELEMENT foo ANY > 
<!ENTITY xxe SYSTEM 
"file:///etc/passwd" >]> 
 
<foo>&xxe;</foo>

В качестве сущности может выступить что-то внешнее, локальный файл, внешний файл по http и т.д. Т.е. это стандарт. Изначально задумывался в благих целях, например нужно отдать XMLку клиенту и взять текущее время с другого сервера (другого xml файла, доступного по HTTP).

Долго не рассуждая, уязвимость очевидна. Используя различные врапперы — file:///, http:// атакующий может получать информацию о системе, сети и т.п. Иногда это может привести и к удаленному выполнению команд, например в PHP существует враппер expect://, который сначала выполняет команду на ОС и возвращает результат её работы. Так что по-умолчанию этой атаке подвержены все стандартные парсеры XML. Одно из решений приходит таким: отключить поддержку внешних сущностей. Тогда атакущий может применить XML бомбу:

<?xml version="1.0"?>
<!DOCTYPE lolz [
 <!ENTITY lol "lol">
 <!ELEMENT lolz (#PCDATA)>
 <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
 <!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
 <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
 <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
 <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
 <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
 <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
 <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
 <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>

И «положить» сервер. Суть в постоянной пересылке сущности на сущность.
Стоит помнить, что это может быть проблема не только сервера, но и клиента (например дектопное ПО клиента парсит XML).
Самый яркий пример использования уязвимости — взлом FaceBook в ноябре 2013, который мог повлечь и выполнение команд от ОС.

Резюмируя:

  • В первую очередь перетестировать все «интересные» ограничения интерфейса;
  • Разобраться со сжатием;
  • Внимательно следит за JS callbac'ами;
  • Криптография. Может быть не только атака на расширение строки, а просто слабый (например — короткий) ключ, или как-то предсказуемый.
  • А еще можно встретить просто зашитые в мобильное приложение API данные. Кстати, их можно найти через сервис http://hackapp.com;
  • XML — XXE;
  • Что угодно ещё.

Презентация:

Демо видео sha padding и XXE:

Автор: BeLove

Источник

Поделиться

* - обязательные к заполнению поля