Данный фиче-баг схож с историй про отправку сообщения в Facebook от любого юзера — известен, но не получил широкую огласку. Получилось так, что нашел я его сам, а уже потом нашел информацию о нём в интернетах. Но обо всем по порядку...
# intro
Темной, холодной и дождливой Питерской ночью обнаружил я забавный баг, которому оказались подвержены приложения от Facebook, Google, ВКонтакте и скорее всего, от многих других производителей. Для его объяснения нам просто необходимо знать немного теории.
1/2 Идеология iOS
Идеологически iOS имеет принцип: никаких левых файлов на устройстве, никакого прямого доступа к контенту и никаких файловых менеджеров. Мы сами все раскидаем по разным категориям и выдадим конечному юзеру в наиболее удобном формате. В итоге в iOS не предусмотрено сохранение произвольных файлов на устройство. Конечно, есть исключения, когда приложения выкачивают себе дополнительный контент с произвольным типом данных (например, карты городов, ресурсы игры и т.п.), но они все друг от друга изолированы и являются лишь локальным хранилищем для приложения.
2/2 Про HTTP
Протокол HTTP очень прост и использует самую очевидную модель. Всё передается обычным plain текстом. Запрос/ответ обязательно содержит заголовок (header) и (возможно) тело (body). Разделены они через обычный перенос строки (пустую строку). Пример запроса для открытия habrahabr.ru:
GET / HTTP/1.1
Host: habrahabr.ru
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:25.0) Gecko/20100101 Firefox/25.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
Ответ:
HTTP/1.1 200 OK
Server: nginx
Date: Tue, 19 Nov 2013 13:48:02 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
Keep-Alive: timeout=25
X-Powered-By: PHP/5.4.21
X-Frame-Options: SAMEORIGIN
Content-Encoding: gzip
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta name = "viewport" content = "width = 1080">
<title>Лучшие за сутки / Посты / Хабрахабр</title>
...
</html>
В ответе, как говорилось выше, две части — заголовки и контент. Так вот, заголовки. Они определяют общую информацию, как вести себя браузеру (статус ответа (страница найдена, не найдена, требуется авторизация и т.п.), показывать ли контент во фрейме и разные другие ситуации. Также существует специальный заголовок
Content-Disposition: attachment
Который описан в RFC 2183 следующим образом
2.2 The Attachment Disposition Type
Bodyparts can be designated `attachment' to indicate that they are
separate from the main body of the mail message, and that their
display should not be automatic, but contingent upon some further
action of the user. The MUA might instead present the user of a
bitmap terminal with an iconic representation of the attachments, or,
on character terminals, with a list of attachments from which the
user could select for viewing or storage.
В типичном браузере это выглядит так, что вы обращаетесь к файлу, а он не отображается (как вы привыкли) а предлагает его сохранить (their display should not be automatic).
# exploit
А теперь давайте соединим два момента выше. Мы обращаемся на устройстве с iOS к файлу, у которого Content-Disposition: attachment. Получается противоречие. Вроде файл надо сохранить (как обычно и привыкли юзеры, видеть окно сохранения на устройство), но и сделать этого нельзя — ведь возможности такой то и нет. Что же сделали в Apple? Они просто игнорируют в мобильном Safari Content-Disposition и отображают файл в браузере.
Что с мобильными приложениями? В клиентах FB, Gmail, VK, везде есть вложения в сообщения. Если отправить, например, html или svg файл, то вызывается компонент webview (читать как Safari) который его и рендерит.
xss.svg
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
<polygon id="triangle" points="0,0 0,50 50,0" fill="#009900" stroke="#004400"/>
<script type="text/javascript">
alert('Redirect to google.ru...');
document.location="http://google.ru";
</script>
</svg>
В итоге, Apple, пытаясь обезопасить и пытаясь улучшить жизнь своим пользователям создала неочевидный момент для разработчиков iOS и, всё-таки, снижение безопасности. Никаких уведомлений, что данный контент будет показан мгновенно. Возвращаясь к RFC, было бы достаточно обычного уведомления что-то типа: «данный контент должен быть сохранён, но он будет показан в браузере», но ничего подобного мы не видим. Исключение все же есть только в одном варианте — Facebook, мобильная версия.
# demo
GMail | ||
VK |
# impact
Как можно заметить, javascript выполняется на другом домене, отличным от основного. Этот домен предназначен только для вложений в сообщения и полностью изолирован от основного сайта. Поэтому реакция, например, Google на это — именно такая (т.е. никакая)
The domain in which you're triggering the XSS — googleuserconent.com — is specifically meant as a compartmentalized «sandbox» for various types of potentially unsafe, user-controlled content. This domain is isolated from any sensitive content due to the same-origin policy.
Что и верно. Например на нём можно и в обычном браузере выполнить XSS.
Но есть другие вектора — фишинг или DoS (например с 0day эксплойтом, который задевает все(?) версии iOS — habrahabr.ru/company/dsec/blog/201602). Да и не все выносят upload контент на отдельные домены (и вот тут уже impact как от полноценной xss на основном домене). Стоит отметить, что нужен именно отдельный домен, а не поддомен.
В общем, будьте осторожны при открытии вложений на IOS устройствах и при разработке веб-приложений с вложениями. Ведь данный фиче-баг прекрасно воспроизведется и в любом браузере iOS (ведь все они за основу используют Safari).