ios - into - javascript injection attack
¿Es Swift vulnerable a la inyección de código? (2)
En última instancia, no hay forma de evitar que alguien secuestre su programa si lo deja ejecutarse en su dispositivo. Hay maneras de hacerlo más difícil, pero no hay manera de hacerlo imposible.
Puedo pensar en estas formas principales de inyectar código en una aplicación:
- swizzling métodos Objective-C con el tiempo de ejecución;
- swizzling los métodos virtuales de Swift analizando el ejecutable y calculando los bits correctos para cambiar;
- modificar destinos de llamadas;
- cambiando los símbolos importados cambiando los objetivos del código auxiliar del símbolo;
- usar dyld para forzar la carga de bibliotecas o cambiar las bibliotecas que carga su programa;
- Reemplazando las bibliotecas con las que tu programa enlaza.
Y no hay una forma 100% efectiva de prevenir ninguno de estos en un entorno que el usuario controle completamente. Debes decidir si estar preocupado o no dependiendo de tu modelo de amenaza.
Métodos Swizzling Objective-C con el tiempo de ejecución
El método swizzling es una técnica en la que se cambia la implementación de un método en tiempo de ejecución con un código arbitrario y diferente (generalmente para un propósito diferente). Los casos de uso comunes son pasar por alto las comprobaciones o los parámetros de registro.
Swizzling en Objective-C fue algo enorme porque el tiempo de ejecución necesita metadatos que identifiquen cada método y cada campo de instancia. No conozco ningún otro idioma que compile en código de máquina nativo y que mantiene esta cantidad de metadatos. Si tienes algo como -[AccessControl validatePassword:]
, lo estás haciendo muy fácil para los malos. Con method_setImplementation
, esto solo está pidiendo que suceda.
Como las clases Swift pueden heredar de las clases de Objective-C, esto sigue siendo algo que debe buscarse. Sin embargo, los nuevos métodos en las clases que heredan de una clase de Objective-C solo están expuestos al tiempo de ejecución de Objective-C si tienen el atributo @objc
(o si la propia clase tiene el atributo @objc
), por lo que esto limita la superficie de ataque en comparación. a Objective-C.
Además, el compilador Swift puede omitir el tiempo de ejecución de Objective-C para llamar, desvirtualizar o los métodos Swift en línea que no estaban marcados como dynamic
, incluso si estaban marcados como @objc
. Esto significa que en algunos casos, el swizzling podría ser posible solo para las llamadas enviadas a través de Objective-C.
Y, por supuesto, es completamente imposible si su clase o método no está expuesto al tiempo de ejecución de Objective-C.
Métodos Swiftling virtuales de Swift analizando el ejecutable y calculando los bits correctos para cambiar
Sin embargo, no necesita el tiempo de ejecución de Objective-C para intercambiar las implementaciones de los métodos. Swift aún tiene tablas virtuales para sus métodos virtuales, y desde febrero de 2015, están ubicadas en el segmento __DATA
del ejecutable. Es escribible, por lo que debería ser posible cambiar los métodos virtuales de Swift si puede descubrir los bits correctos para cambiar. No hay una API conveniente para esto.
Las clases de C ++ pueden modificarse de manera similar, pero los métodos Swift son virtuales de forma predeterminada, la superficie de ataque es mucho más grande. El compilador puede desvirtualizar los métodos como una optimización si no encuentra una anulación, pero confiar en las optimizaciones del compilador como una característica de seguridad no es responsable.
Por defecto, los ejecutables Swift desplegados son strip ped. La información para símbolos no public
/ open
se descarta, y esto hace que la identificación de los símbolos que desea cambiar sea mucho más difícil en comparación con Objective-C. Public
símbolos Public
/ open
no se eliminan porque se supone que otros clientes de código externo pueden necesitarlos.
Sin embargo, si alguien descubre qué función de implementación quiere cambiar, todo lo que tienen que hacer es escribir la dirección de la nueva implementación en la ranura de la tabla virtual correcta. Probablemente necesitarán hacer su propio analizador de Mach-O, pero esto ciertamente no está fuera del alcance de las personas que hacen cosas como Cycript.
Finalmente, los métodos final
reducen este riesgo porque el compilador no necesita llamarlos a través de vtable. Además, los métodos de struct
nunca son virtuales.
Modificación de destinos de llamada
Si todo lo demás falla, su atacante aún puede recorrer el código de su máquina y cambiar el bl
o call
operandos de instrucciones a cualquier lugar que deseen. Esto es más complicado y bastante difícil / imposible de obtener al 100% con un método automatizado, especialmente si faltan símbolos, pero alguien determinado lo suficiente podrá hacerlo. Usted decide si alguien finalmente valdrá la pena hacerlo para su aplicación.
Esto funciona para métodos virtuales y no virtuales. Sin embargo, es extremadamente difícil hacerlo cuando el compilador en línea llama.
Cambia los símbolos importados cambiando los objetivos del símbolo
Cualquier símbolo importado, independientemente del idioma con el que se ha escrito y del idioma desde el que se utiliza, es vulnerable a swizzling. Esto se debe a que los símbolos externos están vinculados en tiempo de ejecución. Cuando se utiliza una función de una biblioteca externa, el compilador genera una entrada en una tabla de búsqueda. Este es un ejemplo de cómo podría ser una llamada a fopen
si devolviera su ejecutable al código 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");
}
La llamada inicial a fopen_stub
encuentra el fopen
real y reemplaza la dirección a la que apunta fopen_stub
. De esa manera, dyld no necesita resolver los miles de símbolos externos utilizados por su programa y sus bibliotecas antes de que comience a ejecutarse. Sin embargo, esto significa que un atacante puede reemplazar fopen_stub
con la dirección de cualquier función a la que les gustaría llamar en su lugar. Esto es lo que hace su ejemplo de Cycript.
Además de escribir su propio enlazador y el enlazador dinámico, su única protección contra este tipo de ataque es no usar bibliotecas o marcos compartidos. Esta no es una solución viable en un entorno de desarrollo moderno, por lo que probablemente tendrá que lidiar con ella.
Podría haber formas de asegurar que los talones vayan donde usted espera que estén, pero sería un poco inestable, y un atacante determinado siempre puede nop
estos controles. Además, no podrá insertar estas comprobaciones antes de que las bibliotecas compartidas no tenga control sobre los símbolos importados de llamadas. Estas verificaciones también serían inútiles si el atacante decidiera simplemente reemplazar la biblioteca compartida con una que controlan.
De manera aparte, los cierres de lanzamiento permiten a dyld 3 reemplazar estas tablas de búsqueda con información previamente enlazada. No creo que los cierres de lanzamiento sean actualmente de solo lectura, pero parece que eventualmente podrían serlo. Si lo son, entonces los símbolos swizzling se harán más difíciles.
Usar dyld para forzar la carga de bibliotecas o cambiar las bibliotecas que carga su programa
Dyld supports la carga forzada de bibliotecas en tu ejecutable. Esta capacidad se puede usar para reemplazar casi cualquier símbolo importado que use su ejecutable. ¿No te gusta el fopen
normal? Escribe un dylib
que lo redefine!
Dyld no cooperará con este método si el ejecutable está marcado como restringido. Hay tres formas de lograr este estado (busque pruneEnvironmentVariables
):
- habilite el bit setuid o el bit setgid en su ejecutable;
- tener el código firmado y tener el derecho "Restringido" de OS X únicamente;
- tener una sección llamada
__restrict
en un segmento llamado__RESTRICT
.
Puede crear la sección __restrict
y el segmento __RESTRICT
utilizando los siguientes "Otros indicadores de vinculador":
-Wl,-sectcreate,__RESTRICT,__restrict,/dev/null
Tenga en cuenta que todos estos son bastante fáciles de romper. Los bits setuid y setgid son triviales de borrar cuando el usuario controla el entorno de ejecución, la firma de un código es fácil de eliminar, y la sección o segmento solo debe ser renombrado para deshacerse del estado restringido también.
Reemplazando las bibliotecas con las que tu programa enlaza
Si todo lo demás falla, un atacante aún puede reemplazar las bibliotecas compartidas que utiliza su ejecutable para hacer que haga lo que quiera. No tienes control sobre eso.
tl; dr
Inyectar código en una aplicación Swift es más difícil de lo que era para una aplicación Objective-C, pero aún es posible. La mayoría de los métodos que se pueden usar para inyectar códigos son independientes del idioma, lo que significa que ningún idioma lo hará más seguro.
En su mayor parte, no hay nada que pueda hacer para protegerse contra esto. Mientras el usuario controle el entorno de ejecución, su código se ejecuta como invitado en su sistema, y pueden hacer casi lo que quieran con él.
Estaba leyendo sobre Cycript y Cydia Substrate y cómo se pueden usar para los ataques de inyección de código en una aplicación de iOS. Este código debería asustarlo si está trabajando en un entorno de alta seguridad. (Ignore la parte / etc / password, solo considere la posibilidad de reemplazar el mensaje original con un mensaje roto ).
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)
Leí un blog (que no guardé) que decía que Swift no era tan vulnerable como Objective-C ya que no era tan dinámico. Por otra parte, también he leído que puede hacer swizzling de método en Swift, por lo que no está claro si Swift ofrece alguna protección contra los ataques de inyección de código.
Entonces, ¿Swift es vulnerable a los ataques de inyección de código?
Estás hablando de inyecciones de código en dispositivos iOS con jailbreak. Bastante simple: el usuario ha eliminado la protección de su sistema operativo, por lo que ahora todo vale. Sin seguridad. Si el usuario no ha eliminado voluntariamente esa protección, es imposible ingresar al espacio de direcciones de una aplicación.