1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/wizardforcel-lpad-zh

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
ch8.md 21 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
gitlife-traslator Отправлено 29.11.2024 08:13 4342e40

Восьмая глава: Использование ARM

Автор: Адитья Гупта.
Переводчик: Летучий дракон.
Лицензия: CC BY-NC-SA 4.0.

В этой главе мы узнаем об основах работы с процессорами ARM и о различных типах уязвимостей, которые существуют в мире ARM. Мы также продолжим использовать эти уязвимости, чтобы получить чёткое представление обо всей ситуации. Кроме того, мы рассмотрим различные атаки на Android root и основные уязвимости, используемые при эксплуатации этих атак. Учитывая, что большинство современных Android-смартфонов используют процессоры на базе ARM, понимание ARM и связанных с ним рисков безопасности имеет решающее значение для пентестеров.

8.1 Введение в архитектуру ARM

ARM — это архитектура, основанная на упрощённом наборе команд (RISC), что означает, что её инструкции намного меньше, чем у машин, основанных на сложном наборе команд (CISC). Процессоры ARM можно найти практически во всех устройствах вокруг нас, таких как смартфоны, телевизоры, электронные книги и другие встроенные устройства.

Всего в ARM есть 16 видимых общих регистров, обозначенных R0–R15. Из них пять используются для специальных целей. Вот эти пять регистров и их названия:

  • R11: указатель кадра (FP);
  • R12: регистр процесса (IP);
  • R13: указатель стека (SP);
  • R14: регистр связи (LR);
  • R15: счётчик программы (PC).

Ниже представлена диаграмма архитектуры ARM:

Из пяти перечисленных мы сосредоточимся на трёх:

  1. Указатель стека (SP): хранит указатель на вершину стека.
  2. Регистр связи (LR): сохраняет адрес возврата при входе в подпроцесс.
  3. Счётчик программы (PC): содержит адрес следующей инструкции для выполнения.

Примечание: здесь важно отметить, что PC всегда указывает на инструкцию, которая должна быть выполнена, а не просто на следующую инструкцию. Это связано с концепцией конвейера, где инструкции выполняются в следующем порядке: извлечение, декодирование и выполнение. Для управления потоком программы нам нужно контролировать значения в PC или LR, причём последнее в конечном итоге приводит к управлению PC.

Режимы выполнения

У ARM есть два разных режима выполнения:

  • Режим ARM: в режиме ARM все инструкции имеют размер 32 бита.
  • Режим Thumb: в режиме Thumb большинство инструкций имеют размер 16 бит.

Режим выполнения определяется состоянием в регистре CPSR. Существует также третий режим, называемый Thumb-2, который представляет собой комбинацию режимов ARM и Thumb. В этой главе мы не будем подробно рассматривать различия между ARM и Thumb, поскольку это выходит за рамки данной книги.

8.2 Создание среды

Прежде чем начать использовать уязвимости ARM, рекомендуется создать среду. Хотя эмулятор Android SDK может запускать ARM-платформу через эмуляцию, большинство смартфонов также основаны на ARM, и мы начнём использовать QEMU (это открытый исходный код для аппаратной виртуализации и эмулятора) для использования уязвимостей ARM.

Чтобы выполнить все шаги на Android-эмуляторе/устройстве, нам необходимо загрузить Android NDK и использовать инструменты, предоставляемые Android NDK, для компиляции нашего двоичного файла для Android. Однако, если вы используете Mac, установка QEMU относительно проста, и вы можете сделать это, набрав brew install qemu. Теперь давайте настроим QEMU в системе Ubuntu. Следуйте этим шагам:

  1. Сначала установите зависимости и загрузите QEMU, как показано ниже:
sudo apt-get build-dep qemu
wget http://wiki.qemu-project.org/download/qemu-1.7.0.tar.bz2
  1. Затем мы просто настраиваем QEMU для целевой платформы ARM и полностью используем его. Поэтому мы распаковываем архив, переходим в каталог и выполняем следующие команды:
./configure --target-list=arm-softmmu
make && make install
  1. После успешной установки QEMU мы можем загрузить образ Debian для ARM-платформы для практики использования. Необходимые файлы для загрузки находятся по адресу http://people.debian.org/~aurel32/qemu/armel/.

  2. Здесь мы загрузим образ диска формата 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
  1. Команда redir просто включает ssh с портом 2222 при входе в удалённую систему. После настройки мы можем войти в экземпляр Debian QEMU с помощью следующей команды:
ssh root@[ip address of Qemu] -p 2222
  1. При входе потребуется ввести имя пользователя и пароль, по умолчанию это root:root. После успешного входа вы увидите экран, подобный следующему: | | | --- | | |

8.3 Простой переполнение буфера на основе стека

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

Давайте рассмотрим пример программы, чтобы увидеть, как мы можем её использовать. В следующем скриншоте у нас есть простая программа с тремя функциями: 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 был добавлен в параметр:

![](img/8-3-4.jpg)

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

![](img/8-3-5.jpg)

## 8.4 Возвратно-ориентированное программирование

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

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

Например, если мы дизассемблируем seed48() во время выполнения программы, мы заметим следующий вывод:

![](img/8-4-1.jpg)

Если мы посмотрим на дизассемблерный код, то увидим, что он содержит инструкцию 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».
1
https://api.gitlife.ru/oschina-mirror/wizardforcel-lpad-zh.git
git@api.gitlife.ru:oschina-mirror/wizardforcel-lpad-zh.git
oschina-mirror
wizardforcel-lpad-zh
wizardforcel-lpad-zh
master