Автор: Адитья Гупта.
Переводчик: Летучий дракон.
Лицензия: CC BY-NC-SA 4.0.
В этой главе мы узнаем об основах работы с процессорами ARM и о различных типах уязвимостей, которые существуют в мире ARM. Мы также продолжим использовать эти уязвимости, чтобы получить чёткое представление обо всей ситуации. Кроме того, мы рассмотрим различные атаки на Android root и основные уязвимости, используемые при эксплуатации этих атак. Учитывая, что большинство современных Android-смартфонов используют процессоры на базе ARM, понимание ARM и связанных с ним рисков безопасности имеет решающее значение для пентестеров.
ARM — это архитектура, основанная на упрощённом наборе команд (RISC), что означает, что её инструкции намного меньше, чем у машин, основанных на сложном наборе команд (CISC). Процессоры ARM можно найти практически во всех устройствах вокруг нас, таких как смартфоны, телевизоры, электронные книги и другие встроенные устройства.
Всего в ARM есть 16 видимых общих регистров, обозначенных R0–R15. Из них пять используются для специальных целей. Вот эти пять регистров и их названия:
Ниже представлена диаграмма архитектуры ARM:
![]() |
Из пяти перечисленных мы сосредоточимся на трёх:
Примечание: здесь важно отметить, что PC всегда указывает на инструкцию, которая должна быть выполнена, а не просто на следующую инструкцию. Это связано с концепцией конвейера, где инструкции выполняются в следующем порядке: извлечение, декодирование и выполнение. Для управления потоком программы нам нужно контролировать значения в PC или LR, причём последнее в конечном итоге приводит к управлению PC.
У ARM есть два разных режима выполнения:
Режим выполнения определяется состоянием в регистре CPSR. Существует также третий режим, называемый Thumb-2, который представляет собой комбинацию режимов ARM и Thumb. В этой главе мы не будем подробно рассматривать различия между ARM и Thumb, поскольку это выходит за рамки данной книги.
Прежде чем начать использовать уязвимости ARM, рекомендуется создать среду. Хотя эмулятор Android SDK может запускать ARM-платформу через эмуляцию, большинство смартфонов также основаны на ARM, и мы начнём использовать QEMU (это открытый исходный код для аппаратной виртуализации и эмулятора) для использования уязвимостей ARM.
Чтобы выполнить все шаги на Android-эмуляторе/устройстве, нам необходимо загрузить Android NDK и использовать инструменты, предоставляемые Android NDK, для компиляции нашего двоичного файла для Android. Однако, если вы используете Mac, установка QEMU относительно проста, и вы можете сделать это, набрав brew install qemu
. Теперь давайте настроим QEMU в системе Ubuntu. Следуйте этим шагам:
sudo apt-get build-dep qemu
wget http://wiki.qemu-project.org/download/qemu-1.7.0.tar.bz2
./configure --target-list=arm-softmmu
make && make install
После успешной установки QEMU мы можем загрузить образ Debian для ARM-платформы для практики использования. Необходимые файлы для загрузки находятся по адресу http://people.debian.org/~aurel32/qemu/armel/
.
Здесь мы загрузим образ диска формата qcow2, основанный на QEMU, который является образом операционной системы, то есть нашей операционной системой будет debian_squeeze_armel_standard.qcow2
. Ядром должно быть vmlinuz-2.6.32-5-versatile
, а файлом RAM-диска должен быть initrd.img-2.6.32-versatile
. После загрузки всех необходимых файлов мы можем запустить экземпляр QEMU с помощью следующих команд:
qemu-system-arm -M versatilepb -kernel vmlinuz-2.6.32-5-versatile -initrd initrd.img-2.6.32-5-versatile -hda debian_squeeze_armel_standard.qcow2 -append "root=/dev/sda1" --redir tcp:2222::22
redir
просто включает ssh с портом 2222 при входе в удалённую систему.
После настройки мы можем войти в экземпляр Debian QEMU с помощью следующей команды:ssh root@[ip address of Qemu] -p 2222
root:root
. После успешного входа вы увидите экран, подобный следующему:
| |
| --- |
| Проще говоря, буфер — это место для хранения данных любого типа. Когда данные в буфере превышают размер самого буфера, происходит переполнение. Затем злоумышленник может провести атаку переполнения, чтобы получить контроль над программой и выполнить вредоносный код.
Давайте рассмотрим пример программы, чтобы увидеть, как мы можем её использовать. В следующем скриншоте у нас есть простая программа с тремя функциями: weak
, ShouldNotBeCalled
и main
. Программа, которую мы пытаемся использовать, выглядит следующим образом:
![]() |
Во время всего выполнения программы функция ShouldNotBeCalled
никогда не вызывается.
Функция уязвимости просто копирует параметр в буфер размером 10 байт.
Как только мы закончим писать программу, мы можем скомпилировать её с помощью GCC, как показано в одной команде ниже. Кроме того, здесь мы отключим рандомизацию адресного пространства (ASLR), просто чтобы немного упростить ситуацию. ASLR — это технология безопасности, реализованная ОС, предназначенная для предотвращения эффективного определения злоумышленником адреса полезной нагрузки и выполнения вредоносных инструкций. В Android реализация ASLR началась с версии 4.0. Вы можете посетить http://www.duosecurity.com/blog/exploit-mitigations-in-android-jelly-bean-4-1
, чтобы узнать больше об Android-безопасности.
echo 0 > /proc/sys/kernel/randomize_va_space
gcc -g buffer_overflow.c -o buffer_overflow
Далее мы можем просто загрузить двоичный файл в отладчик GNU (сокращённо GDB) и начать отлаживать его, как показано в следующей команде:
gdb -q buffer_overflow
Теперь мы можем использовать команду disass
для дизассемблирования конкретной функции, здесь это ShouldNotBeCalled
:
![]() |
Как видно из скриншота выше, функция ShouldNotBeCalled
начинается с адреса памяти 0x00008408
. Если мы посмотрим на дизассемблирование функции main
, мы увидим, что функция уязвимости вызывается по адресу 0x000084a4
и возвращается по адресу 0x000084a8
. Таким образом, из-за того, что программа входит в функцию уязвимости и использует уязвимую strcpy
, функция не проверяет размер строки, которую она копирует, и если мы сможем контролировать адрес возврата подпроцесса во время выполнения функции уязвимости, мы сможем контролировать весь поток программы.
Здесь цель состоит в том, чтобы оценить, когда адрес возврата будет перезаписан, а затем вставить адрес ShouldNotBeCalled
, чтобы вызвать эту функцию. Давайте начнём с запуска программы с длинным параметром и посмотрим, что произойдёт. Прежде чем это сделать, нам также нужно установить точки останова на функции уязвимости и вызове strcpy
.
b vulnerable
b *<address of the strcpy call>
После установки точек останова мы можем запустить нашу программу с параметром AAAABBBBCCCC
, чтобы посмотреть, как она перезаписывается. Мы замечаем, что она попадает в первую точку останова при вызове функции уязвимости, а затем во вторую точку останова после вызова strcpy
. Как только он достигает точки останова, мы можем использовать команду x
для анализа стека и указать адрес из SP, как показано на следующем скриншоте:
![]() |
Мы видим, что стек уже был перезаписан нашим буфером (ASCII: 41 представляет A, 42 представляет B и так далее). Из скриншота выше мы всё ещё нуждаемся в четырёх дополнительных байтах, чтобы перезаписать адрес возврата, в данном случае 0x000084a8
.
Итак, окончательная строка представляет собой 16-байтовый мусор, за которым следует адрес ShouldNotBeCalled
. Перевод текста на русский язык:
«AAAABBBBCCCCDDDD\x38\x84»
На следующем скриншоте мы видим, что начальный адрес IShouldNeverBeCalled был добавлен в параметр:

Обратите внимание, что из-за использования малой конечной структуры байты записываются в обратном порядке. После запуска программы мы можем увидеть вызов функции ShouldNotBeCalled, как показано на следующем скриншоте:

## 8.4 Возвратно-ориентированное программирование
В большинстве случаев нам не нужно вызывать другую функцию, которая уже существует в программе. Вместо этого нам нужно разместить shellcode в нашем векторе атаки, который будет выполнять любые вредоносные действия, указанные в shellcode. Однако в большинстве устройств на базе ARM область памяти является неисполняемой, что препятствует размещению и выполнению shellcode.
Поэтому злоумышленник должен полагаться на так называемое возвратно-ориентированное программирование (ROP), которое представляет собой простую цепочку инструкций из разных частей памяти, которая в конечном итоге выполняет наш shellcode. Эти фрагменты также называются ROP gadget. Чтобы связать ROP gadget, нам нужно найти фрагмент инструкции перехода, который позволит нам перейти в другое место.
Например, если мы дизассемблируем seed48() во время выполнения программы, мы заметим следующий вывод:

Если мы посмотрим на дизассемблерный код, то увидим, что он содержит инструкцию ADD, за которой следуют инструкции POP и BX, что является идеальным ROP gadget. Здесь злоумышленник может подумать о том, чтобы использовать его в качестве ROP gadget, сначала перейти к инструкции POP, управляющей r4, затем поместить значение, меньшее 6, в r4 и поместить значение инструкции ADD в LR. Таким образом, когда мы вернёмся к ADD, то есть R0 = R4 + 6, у нас будет адрес /bin/sh, и мы сможем указать любой мусорный адрес для R4 и адрес system() для LR.
Это означает, что мы в конечном итоге перейдём к system(), используя параметр /bin/sh, что выполнит оболочку. Аналогичным образом мы можем создать любой ROP gadget и заставить его выполнять всё, что нам нужно. Поскольку ROP является одной из самых сложных тем в разработке, настоятельно рекомендуется попробовать самостоятельно, проанализировать дизассемблерный код и построить уязвимость.
## 8.5 Использование Android root
С ранних версий Android уязвимости Android root начали появляться в каждой последующей версии и различных версиях от производителей устройств Android. Проще говоря, Android root — это получение доступа к устройству, который по умолчанию не предоставляется производителем устройства пользователю. Эти атаки с использованием root основаны на различных уязвимостях в системе Android. Вот некоторые из них:
+ Exploid: основан на уязвимости CVE-2009-1185 в udev, компоненте Android, отвечающем за USB-подключение, который проверяет, исходит ли сообщение Netlink (тип сообщения, которое соединяет ядро Linux с пользователем) от исходного источника или подделано злоумышленником. Поэтому злоумышленник может просто отправить сообщение udev из пользовательского пространства и повысить свои привилегии.
+ Gingerbreak: ещё одна уязвимость, основанная на уязвимости в vold, аналогичная уязвимости Exploid.
+ RageAgainstTheCage: эта уязвимость основана на RLIMIT_NPROC, который определяет максимальное количество процессов, которые могут быть созданы для пользователя при вызове функции setuid. Программа adb запускается с правами root; затем она использует вызов setuid() для снятия привилегий. Однако, если достигнут максимальный предел процессов в соответствии с RLIMIT_NPROC, программа не сможет вызвать setuid(), чтобы снять привилегии, и adb продолжит работать с правами root.
+ Zimperlich: использует ту же концепцию, что и RageAgainstTheCage, но зависит от процесса zygote для снятия прав root.
+ KillingInTheNameOf: использует уязвимость в интерфейсе ashmem (менеджер общей памяти), который используется для изменения значения ro.secure, определяющего состояние root устройства.
Эти некоторые из наиболее известных уязвимостей Android, используемых для получения root-прав на устройствах Android.
## Заключение
В этой главе мы рассмотрели различные способы использования Android и ARM. Надеюсь, эта глава станет хорошим началом для всех, кто хочет более глубоко изучить использование ARM».
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.