Подтвердить что ты не робот

Является ли Swift уязвимым для инъекции кода?

Я читал о Cycript и Cydia Substrate и как их можно использовать для атак на инъекции кода в приложении iOS. Код, подобный этому, должен напугать вас, если вы работаете в среде с высокой степенью безопасности. (Игнорируйте часть /etc/password, просто рассмотрите возможность заменить originalMessage на crackedMessage.)

cy# MS.hookFunction(fopen, function(path, mode) {
cy>     if (path == "/etc/passwd")
cy>         path = "/var/passwd-fake";
cy>     var file = (*oldf)(path, mode);
cy>     log.push([path, mode, file]);
cy>     return file;
cy> }, oldf)

Я прочитал один блог (который я не сохранил), который сказал, что Swift не был так уязвим, как Objective-C, поскольку он не был таким динамичным. Опять же, я также прочитал, что вы можете сделать метод swizzling в Swift, поэтому мне непонятно, если Swift предлагает любую защиту от атак с использованием кода.

Итак, уязвим ли Swift к атакам инъекций кода?

4b9b3361

Ответ 1

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

Я могу думать об этих основных способах ввода кода в приложение:

  • swizzling Objective-C методы со временем выполнения;
  • swizzling виртуальные методы Swift путем анализа исполняемого файла и определения правильных битов для изменения;
  • изменение целей вызова;
  • swizzling импортированные символы, изменяя цели символа-заглушки;
  • используя dyld для загрузки загружаемых библиотек или изменения тех библиотек, которые загружает ваша программа;
  • заменить библиотеки, с которыми связана ваша программа.

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

Методы Swizzling Objective-C со временем выполнения

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

Swizzling в Objective-C было огромной вещью, потому что во время выполнения нужны метаданные, которые идентифицируют каждый метод и каждое поле экземпляра. Я не знаю другого языка, который компилируется в собственный машинный код и который поддерживает эти метаданные. Если у вас есть что-то вроде -[AccessControl validatePassword:], вы просто делаете это очень легко для плохих парней. С method_setImplementation это просто попрошайничество.

Поскольку классы Swift могут наследовать классы Objective-C, это все равно что-то, что нужно искать. Однако новые методы классов, которые наследуются от класса Objective-C, отображаются только во время выполнения Objective-C, если они имеют атрибут @objc (или если сам класс имеет атрибут @objc), поэтому это ограничивает атаку по сравнению с Objective-C.

Кроме того, компилятор Swift может обойти среду выполнения Objective-C для вызова, девиртуализации или встроенных методов Swift, которые не были отмечены dynamic, даже если они были отмечены @objc. Это означает, что в некоторых случаях swizzling может быть возможен только для вызовов, отправленных через Objective-C.

И, конечно, это совершенно невозможно, если ваш класс или метод не подвергаются работе Objective-C.

Swizzling виртуальные методы Swift путем разбора исполняемого файла и определения правильных битов для изменения

Тем не менее, вам не нужна среда выполнения Objective-C для замены методов обмена. У Swift все еще есть виртуальные таблицы для своих виртуальных методов, и по состоянию на февраль 2015 года они расположены в сегменте __DATA исполняемого файла. Он доступен для записи, поэтому должно быть возможно swizzle виртуальные методы Swift, если вы можете определить правильные биты для изменения. Для этого нет удобного API.

C++ аналогичным образом можно модифицировать классы, но Swift-методы по умолчанию являются виртуальными, поверхность атаки намного больше. Компилятору разрешено девиртуализировать методы как оптимизацию, если он не находит переопределения, но полагаться на оптимизацию компилятора как функцию безопасности не несет ответственности.

По умолчанию развернутые исполняемые файлы Swift: strip ped. Информация для символов public/open отбрасывается, и это позволяет идентифицировать символы, которые вы хотите изменить, намного сложнее, чем Objective-C. Символы public/open не разделяются, поскольку предполагается, что им могут понадобиться другие клиенты внешнего кода.

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

Наконец, методы final уменьшают этот риск, потому что компилятор не должен вызывать их через vtable. Кроме того, методы struct никогда не являются виртуальными.

Изменение целей вызова

Если все остальное не удастся, ваш злоумышленник все равно может пройти через ваш машинный код и изменить операнды команд bl или call в любом месте, где они хотели бы лучше. Это более привлекательно и довольно сложно/невозможно получить 100% прав с помощью автоматизированного метода, особенно если символы отсутствуют, но кто-то определил достаточно, сможет это сделать. Вы решаете, будет ли кто-то в конечном итоге сочтет нужным сделать это для вашего приложения.

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

Swizzling импортированные символы путем изменения целей символа-заглушки

Любой импортированный символ, независимо от языка, на котором он был написан, и языка, из которого он используется, уязвим для swizzling. Это связано с тем, что внешние символы привязаны во время выполнения. Всякий раз, когда вы используете функцию из внешней библиотеки, компилятор генерирует запись в таблице поиска. Это пример того, как может выглядеть вызов fopen, если вы вернули свой исполняемый код в код C:

FILE* locate_fopen(const char* a, const char* b) {
    fopen_stub = dyld->locate("fopen"); // find actual fopen and replace stub pointer to it
    return fopen_stub(a, b);
}

FILE* (*fopen_stub)(const char*, const char*) = &locate_fopen;

int main() {
    FILE* x = fopen_stub("hello.txt", "r");
}

Первоначальный вызов fopen_stub находит фактический fopen и заменяет его адресом, на который указывает fopen_stub. Таким образом, dyld не нужно разрешать тысячи внешних символов, используемых из вашей программы и ее библиотек, прежде чем она начнет работать вообще. Однако это означает, что злоумышленник может заменить fopen_stub адресом любой функции, которую они хотели бы назвать. Это пример вашего Cycript.

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

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

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

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

Dyld поддерживает принудительные загрузки библиотек в ваш исполняемый файл. Эта возможность может использоваться для замены практически любого импортированного символа, который использует ваш исполняемый файл. Не нравится обычный fopen? Напишите dylib, который переопределяет его!

Dyld не будет сотрудничать с этим методом, если исполняемый файл отмечен как ограниченный. Для достижения этого статуса три способа (посмотрите pruneEnvironmentVariables):

  • включить бит setuid или бит setgid в исполняемом файле;
  • подпишитесь на кодовую подпись и получите право на использование только для ОС с ограниченным доступом;
  • имеют раздел под названием __restrict в сегменте, называемом __restrict.

Вы можете создать раздел __restrict и сегмент __restrict, используя следующие "Другие флаги компоновщика":

-Wl,-sectcreate,__RESTRICT,__restrict,/dev/null

Обратите внимание, что все это довольно легко сломать. Биты setuid и setgid тривиальны для очистки, когда пользователь управляет средой исполнения, легко можно удалить сигнатуру кода, и раздел или сегмент просто нужно переименовать, чтобы избавиться от ограниченного статуса.

Замена библиотек, которые ваша программа связывает с

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

TL;DR

Код инъекции в приложении Swift сложнее, чем при использовании приложения Objective-C, но это все еще возможно. Большинство методов, которые могут использоваться для ввода кода, не зависят от языка, а это означает, что ни один язык не сделает вас более безопасным.

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

Ответ 2

Вы говорите об инъекциях кода на jailbroken устройствах iOS. Довольно просто: пользователь удалил защиту ОС, поэтому теперь все идет. Нет безопасности. Если пользователь не добровольно удалил эту защиту, то попасть в адресное пространство приложения невозможно.