Программирование, ориентированное на sigreturn

Программирование, ориентированное на sigreturn (англ. sigreturn-oriented programming, SROP) — это техника эксплуатации уязвимостей в компьютерной безопасности, позволяющая злоумышленнику выполнять произвольный код несмотря на такие механизмы защиты, как неисполняемая память и подпись кода[1]. Эта техника была впервые представлена на 35-м симпозиуме IEEE по безопасности и приватности в 2014 году, где получила награду за лучшую студенческую работу[2]. Методика основана на тех же принципах, что и программирование, ориентированное на возврат (ROP): при контроле над стеком вызовов (например, посредством переполнения буфера стека) злоумышленник может влиять на поток управления программой при помощи коротких цепочек инструкций — так называемых гаджетов. Атака реализуется через подделку структуры sigcontext на стеке и замену исходного адреса возврата адресом гаджета, который вызывает системный вызов sigreturn[3]. Нередко для успешной реализации атаки требуется всего один гаджет, который может находиться по фиксированному адресу, что значительно упрощает процесс. Обычно подготовка такой атаки проще и более переносима, чем при традиционном ROP[1].

Программирование, ориентированное на sigreturn, можно рассматривать как пример странной машины, так как оно позволяет выполнять команды вне рамок изначальной спецификации программы[1].

Предпосылки

Программирование, ориентированное на sigreturn (SROP), похоже на программирование, ориентированное на возврат (ROP), поскольку также использует повторное использование кода для запуска кода вне исходного потока управления программой. Для этого атакующий обычно осуществляет атаку на стек, например через переполнение буфера стека, чтобы перезаписать адрес возврата в стеке вызовов.

Эксплойты на «перепрыгивании по стеку»

Когда применяются механизмы вроде предотвращения выполнения данных, злоумышленник не может просто разместить шелл-код на стеке и передать управление на него посредством подстановки адреса возврата. Подобная защита не позволяет исполнять любой код из областей памяти, помеченных как доступные для записи, но не для выполнения. Поэтому нападающий должен использовать уже существующий в памяти код.

Большинство программ не содержит функций, позволяющих напрямую выполнить требуемое действие (например, получить доступ к оболочке), однако нужные инструкции могут быть рассеяны по памяти[4].

Для функционирования ROP-гаджета последовательность инструкций должна заканчиваться инструкцией RET. Это позволяет составить последовательность адресов гаджетов в стеке — после выполнения RET в каждом гаджете управление передаётся следующему адресу в списке.

Механизм обработки сигналов

undefined

Уязвимость, используемая SROP, связана с тем, как сигналы Unix обрабатываются в большинстве POSIX-совместимых систем. При доставке сигнала ядро должно переключить контекст на обработчик сигнала. Для этого оно сохраняет текущий контекст выполнения в специальный фрейм на стеке[4]. В стек помещается архитектурно-специфичная версия структуры sigcontext, содержащая значения регистров на момент переключения. По завершению работы обработчика сигнала вызывается системный вызов sigreturn().

Вызов системного вызова sigreturn позволяет легко установить нужные значения регистров с помощью одного гаджета, который, как правило, присутствует во всех системах[1].

Отличия от ROP

Есть ряд факторов, отличающих SROP-эксплойт от классической атаки программированием, ориентированным на возврат.[5]

Во-первых, ROP зависит от наличия подходящих гаджетов, которые могут значительно различаться в разных исполняемых файлах, делая ROP-цепочки мало переносимыми. Рандомизация адресного пространства (ASLR) затрудняет использование гаджетов без утечки информации о точных адресах в памяти.

Хотя существуют компиляторы ROP, обладающие Тьюринг-полнотой[6], создание ROP-цепочки на практике — задача нетривиальная[5].

Эксплойты SROP обычно легко переносятся между разными исполняемыми файлами и позволяют просто устанавливать значения регистров, что при ROP невозможно или требует сложных действий, если нужные гаджеты отсутствуют[4]. Кроме того, SROP требует минимального числа гаджетов и позволяет создавать эффективные шеллкоды посредством цепочек системных вызовов. Эти гаджеты всегда содержатся в памяти, а в некоторых случаях находятся по фиксированным адресам:[5]

Список гаджетов для различных систем
ОС ASLR Гаджет Карта памяти Фиксированное расположение в памяти
Linux i386 да sigreturn [vdso]
Linux < 3.11 ARM нет sigreturn [vectors] 0xffff0000
Linux < 3.3 x86-64 нет syscall&return [vsyscall] 0xffffffffff600000
Linux ≥ 3.3 x86-64 да syscall&return libc
Linux x86-64 да sigreturn libc
FreeBSD 9.2 x86-64 нет sigreturn 0x7ffffffff000
Mac OSX x86-64 да sigreturn libc
iOS ARM да sigreturn libsystem
iOS ARM да syscall & return libsystem

Атаки

Linux

Пример гаджета, необходимого для SROP-эксплойтов, всегда можно найти в области памяти виртуальный объект динамической компоновки (VDSO) на системах x86-Linux:

__kernel_sigreturn proc near:
pop     eax
mov     eax, 77h
int     80h  ; LINUX - sys_sigreturn
nop
lea     esi, [esi+0]
__kernel_sigreturn endp

В некоторых версиях ядра Linux ASLR может быть отключён установкой неограниченного лимита на размер стека[7], что позволяет обойти ASLR и получать доступ к гаджетам, расположенным в VDSO.

В ядрах Linux до версии 3.3 подходящий гаджет также можно найти на странице vsyscall, которая служит для ускорения выполнения некоторых системных вызовов, востребованных устаревшими программами, и всегда размещается по фиксированному адресу.

Тьюринг-полнота

С помощью гаджетов можно осуществлять запись в содержимое стековых фреймов, по сути реализуя самомодифицирующийся код. Используя данный подход, можно реализовать простую виртуальную машину, пригодную в качестве целевого языка для Тьюринг-полной компиляции. Такой пример приведён в статье Эрика Босмана, где демонстрируется построение интерпретатора языка, аналогичного Brainfuck: программа обладает счётчиком команд PC, указателем памяти P и временным регистром для 8-битного сложения A. Соответственно, на базе SROP можно строить сложные бэкдоры и обфусцированные атаки[1].

Защита и противодействие

Существует ряд методов противодействия атакам SROP, основанных на рандомизации адресного пространства, канарейках или теневом стеке.

Рандомизация адресного пространства

Рандомизация усложняет использование необходимых гаджетов, делая их расположение в памяти непредсказуемым.

Cookies сигналов

Метод защиты SROP, называемый cookies сигналов, основан на верификации структуры sigcontext с помощью случайного значения (cookie), полученного посредством побитового XOR с адресом стека для хранения структуры. Таким образом, системный вызов sigreturn может проверять наличие корректного cookie в нужном адресе, что эффективно противодействует SROP при минимальном снижении производительности.[1][8]

Эмуляция vsyscall

В ядрах Linux версии выше 3.3 интерфейс vsyscall эмулируется, и любые попытки исполнения кода напрямую в этой области приводят к исключению[9].

RAP

Grsecurity — это проект патчей для ядра Linux, направленных на усиление безопасности[10]. В его состав входит return-address protection (RAP), обеспечивающий защиту от атак повторного использования кода[11].

CET

С 2016 года Intel разрабатывает технологию контроля потока исполнения (CET) для предотвращения атак с перепрыгиванием по стеку. CET реализует теневой стек в оперативной памяти, содержащий только адреса возврата, защищённые посредством механизмов управления памятью в процессоре.

Примечания

  1. 1 2 3 4 5 6 Framing Signals - A Return to Portable Shellcode // 2014 IEEE Symposium on Security and Privacy. — 2014. — P. 243–358. — ISBN 978-1-4799-4686-0. — doi:10.1109/SP.2014.23.
  2. Award Papers of the 2014 IEEE Symposium on Security and Privacy. IEEE security. IEEE Computer Society's Technical Committee on Security and Privacy. Дата обращения: 17 июня 2016.
  3. SIGRETURN(2) - Linux manual page. Дата обращения: 21 июня 2016.
  4. 1 2 3 Sigreturn-oriented programming and its mitigation. Дата обращения: 20 июня 2016.
  5. 1 2 3 Framing Signals: a return to portable shellcode. Дата обращения: 21 июня 2016.
  6. ROPC — Turing complete ROP compiler (part 1) (англ.) (12 декабря 2013). Дата обращения: 21 июня 2016.
  7. CVE-2016-3672 - Unlimiting the stack not longer disables ASLR. Дата обращения: 20 июня 2016.
  8. Sigreturn-oriented programming and its mitigation. Дата обращения: 20 июня 2016.
  9. On vsyscalls and the vDSO. Дата обращения: 20 июня 2016.
  10. Linux Kernel Security (SELinux vs AppArmor vs Grsecurity). Дата обращения: 21 июня 2016.
  11. RAP: RIP ROP. Дата обращения: 20 июня 2016.