Программирование, ориентированное на 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 в каждом гаджете управление передаётся следующему адресу в списке.
Механизм обработки сигналов
Уязвимость, используемая 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 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.
- ↑ 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.
- ↑ SIGRETURN(2) - Linux manual page. Дата обращения: 21 июня 2016.
- ↑ 1 2 3 Sigreturn-oriented programming and its mitigation. Дата обращения: 20 июня 2016.
- ↑ 1 2 3 Framing Signals: a return to portable shellcode. Дата обращения: 21 июня 2016.
- ↑ ROPC — Turing complete ROP compiler (part 1) (англ.) (12 декабря 2013). Дата обращения: 21 июня 2016.
- ↑ CVE-2016-3672 - Unlimiting the stack not longer disables ASLR. Дата обращения: 20 июня 2016.
- ↑ Sigreturn-oriented programming and its mitigation. Дата обращения: 20 июня 2016.
- ↑ On vsyscalls and the vDSO. Дата обращения: 20 июня 2016.
- ↑ Linux Kernel Security (SELinux vs AppArmor vs Grsecurity). Дата обращения: 21 июня 2016.
- ↑ RAP: RIP ROP. Дата обращения: 20 июня 2016.