2 года назад я выступал на конференции CodeFest с темой «Пентест на стероидах. Автоматизируем процесс» и делал пересказ выступления в качестве статьи. В этом году я с большим удовольствием снова принял участие в конференции и выступил с темой «BlackBox тестирование безопасности клиент-серверного API» и, видимо уже в качестве традиции, также делаю пересказ выступления.
Уязвимости в API встречаются. Правда.
О чем разговор?
Разговор про API, но API бывает разным — API операционной системы, сайта, дектопной программы, да хоть чего. Вкратце — API это обращение к методам чего-либо (например в случае ОС — запись в файл) через определенный метод. И запуск какого-нибудь файла с т.ч. зрения разработки произойдет схожим образом (тоже через API метод ОС), хотя результат выполнения команды совершенно разный. В веб-технологиях многие тоже реализуют API к своим проектам, и если на пальцах: можно отправить сообщение в соц. сети зайдя на сайт, а можно — сформировав специальный HTTP запрос. И при реализации подобного альтернативного использования функционала допускаются (как и везде) ошибки в безопасности. Статья как раз о специфичных ошибках при реализации подобного функционала в веб-проектах.
От интерфейса к API методам
Разберем уязвимость в API на youtube, которая не получила широкой огласки. Youtube запрещает из интерфейса использовать спец. символы типа < и > (которые нужны для html тэгов и проведения атаки XSS) в названии роликов. Но! Если найти API и попробовать изменить название этого же видео, то все пройдет успешно. Как итог: нет, xss не выполнилась на странице youtube. Но выполнилась в письме на gmail (когда приходит письмо с роликом), что еще критичнее.
Выводы? Логично — проблема в разной работе методов. А вот почему она случилась — это более интересный вопрос. Здесь бы я выделил следующее:
- Практически все большие проекты используют ООП и паттерны подобные MVC. И реализации API должны были просто отнаследоваться от метода в интерфейсе и забрать все его ограничения. Значит или нет подобного шаблона и метод писался с нуля (бред? кто знает) или кастомный «хак» в работе интерфейса;
- Кастомный хак? Проблема разработчика, конечно. Но еще и тимлида, нужно постоянно доносить до разработчиков, где и что нужно реализовывать. Т.е. четко для всех разобрать извечный тред: где в процессе разработки мы валидируем данные (в модели, сервисе, контроллере, где-то еще). Это последствия проблемы применения сложных паттернов при разработке, которые тимлид то понимает нормально, а разработчики не дотягивают и часть валидаций в проекте в одном месте, часть — в другом. Для API корректно отнаследовались, но получили уязвимость.
Что для тестировщиков?
- Нужно вообще забыть всё, что мы знали об интерфейсе (его ограничениях) и тестировать проект с нуля;
- Проверять подмену параметров, яркий пример со скрином про FaceBook — из интерфейса нельзя было подменить id отправителя, а вот из API — можно;
- Проверять «стандартные» атаки, типа sqli/xss;
- Если есть автотесты — то это круто. Можно заменить стандартный пейлоад типа 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 — наши новые данные
При отправке одного и того же параметра например 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;©right;</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