Your SlideShare is downloading. ×
0
Seguridad de Código
Basada en tecnología vírica
Jesús Olmos González
Departamento de Auditoría
jolmos@isecauditors.com
Esquema
1. Inseguridad de código y medidas actuales.
2. Análisis de la raíz del problema.
3. Regeneración del código máqui...
1. Inseguridad de código y medias
Inseguridad de código
y
medidas actuales
(las medidas actuales no solucionan el
problema...
● En el cyberespacio todo es código y datos.
● Todo es automatizable.
● El código también es usuario de internet:
– Puede ...
1. Inseguridad de código y medidas
● El atacante siempre es código.
● Puede ser código controlado por una persona:
– Neces...
1. Inseguridad de código y medidas
● La computadora es una máquina de ejecutar
código.
● Ni el procesador ni el sistema op...
1. Inseguridad de código y medidas
1. Inseguridad de código y medidas
1. Inseguridad de código y medidas
char busca[MAXIMA_BUSQUEDA];
char log_busquedas[LOG_NAME_SIZE];
● El orden no determina...
1. Inseguridad de código y medidas
char busca[MAXIMA_BUSQUEDA];
void (*fptr)(void);
● Redirección de la ejecución.
● Where...
1. Inseguridad de código y medidas
¿Porqué se puede
saltar de una variable
a otra?
PAX-GR etc...
ADMIN
GCC
Pues porque no ...
1. Inseguridad de código y medidas
● El firewall tapa el problema:
– Suelen ser efectivos en su labor de tapa.
– Más códig...
1. Inseguridad de código y medidas
● El firewall determina quien tiene acceso a que
servicio
1. Inseguridad de código y medidas
● El firewall de aplicación web tapa los fallos de
código:
– Siempre hay fallos de códi...
1. Inseguridad de código y medidas
● El sistema antivirus, frena lo que ya ha entrado:
– Puede dificultar la subida de her...
1. Inseguridad de código y medidas
● El sistema IDS no corrige el problema:
– Es importante la detección.
– Si se detecta,...
1. Inseguridad de código y medidas
● El sistema IPS puede tapar las
vulnerabilidades:
– Puede impedir el ataque, tomando m...
1. Inseguridad de código y medidas
● La teoría X^W no impide el bof:
– Es lógico y necesario.
– Dificulta la explotación e...
1. Inseguridad de código y medidas
● Direcciones aleatorias:
– Afecta al rendimiento.
– Dificulta la explotación en alguno...
1. Inseguridad de código y medidas
● Canary values:
– Afectan al rendimiento.
– Normalmente no es viable realizar brutefor...
1. Inseguridad de código y medidas
● Correctores de código fuente:
– Analizan la raíz del problema.
– El código fuente tie...
2. Anaísis de la raíz del problema
Análisis de la raíz
del
problema
(que sucede tras la compilación)
2. Análisis de la raíz del problema
● Antes de la compilación se tiene mucha
información.
tipo descripcion[cantidad];
size...
2. Análisis de la raíz del problema
● Después de la compilación no tenemos
cantidades, ni tipos, ni mucho menos
descripcio...
2. Análisis de la raíz del problema
● No hay límites.
● El código dicta como utilizar los datos.
● ¿Cómo diferenciar si el...
2. Análisis de la raíz del problema
● Imaginemos la pila en tiempo de ejecución.
● ¿Como podemos ver que el strcpy puede
i...
2. Análisis de la raíz del problema
● La pila tendrá 3 estados:
– Inicial.
– Tras ejecutarse fptr = (void(*)(void))main;
–...
2. Análisis de la raíz del problema
2. Análisis de la raíz del problema
● Mirando los datos no se pueden ver los límites.
● Es posible imaginarlos con técnica...
2. Análisis de la raíz del problema
2. Análisis de la raíz del problema
CONOCIDO APROXIMADO
0xffffffec(%ebp) <=0xec bytes(%esp) 4bytes
● Las tablas varían seg...
2. Análisis de la raíz del problema
● Para corregir una línea de código se tiene en
cuenta el estado de las tablas en ese ...
2. Análisis de la raíz del problema
2. Análisis de la raíz del problema
CONOCIDO APROXIMADO
0xfffffffc(%ebp) 4 bytes
● Las tablas varían según se va ejecutand...
2. Análisis de la raíz del problema
● Hay que tener en cuenta que el código puede ir
reservando más memoria o destruyendo ...
2. Análisis de la raíz del problema
● Las aproximaciones también son de gran
ayuda.
3. Regeneración del código máquina
Regeneración
del código
máquina.
(como corregir el código, conociendo los límites reale...
3. Regeneración del código máquina
3. Regeneración del código máquina
● El salto que realiza el parche es relativo.
● Sin optimización habría realizado un st...
3. Regeneración del código máquina
● ¿Cómo distinguir que es cada llamada externa?●
strcpy
_start PLT (XR)
GOT (RW)
main
s...
3. Regeneración del código máquina
3. Regeneración del código máquina
● En win32 se puede buscar directamente el
nombre de la api en la AddressOfNames de la
...
3. Regeneración del código máquina
3. Regeneración del código máquina
● Los únicos límites detectados serían los de la
pila (esp y ebp) en este caso son 100%...
3. Regeneración del código máquina
● El regenerador,
detectaría inc e i
de manera que el
primer bof no se
podría dar.
3. Regeneración del código máquina
● El analizador de código podría debugar:
– En linux PTRACE_SINGLESTEP
– No se necesita...
3. Regeneración del código máquina
● El analizador de código podría NO debugar:
– Se podría recorrer el código desde el ma...
4. Infecciones y cold-patching
Infecciones
y
cold-patching
(Podemos aprender de los virus)
4. Infecciones y cold-patching
● Según como se regenere el código, este puede
acabar ocupando más.
● Si el código crece, t...
4. Infecciones y cold-patching
● Infección de memoria (hot-patch)
– Hay que desproteger el area de código.
– El cambio no ...
4. Infecciones y cold-patching
● Infección de disco (cold-patch)
– Hay que mantener los alineamientos y distancias
relativ...
4. Infecciones y cold-patching
● O se combina cold-patch con hot-patch o sólo se
realiza cold-patch.
● Se ha de poder rege...
4. Infecciones y cold-patching
● Infección mediante overlay:
– Sencilla y efectiva aunque drástica.
– Copia todo el código...
4. Infecciones y cold-patching
● Infección mediante alargamiento de .text
– Se necesita relocatar definiciones de seccione...
4. Infecciones y cold-patching
● Infección mediante creación de .patch
– Se crea sección nueva y se registra en shstrtab.
...
4. Infecciones y cold-patching
● La creación de un segundo segmento de código
no es viable:
– Se controla desde kernel.
– ...
4. Infecciones y cold-patching
● Procedimiento de creación de .patch
1.Remapear con el tamaño del fichero+parche+1
registr...
4. Infecciones y cold-patching
7. Desplazamiento lógico de las secciones inferiores a
.patch (offset y virtual)
8. Desplaz...
4. Infecciones y cold-patching
Registro elf header
Tabla de segmentos
CODE
DATA
Tabla de secciones
.text
.plt
.got
.patch
5. Prueba de comcepto
Prueba
de
Concepto
(POC)
4. Infecciones y cold-patching
¿ Preguntas ?¿ Preguntas ?
Autopresentación
Jesús Olmos González
Internet Security Auditors
Departamento de Auditoría
jolmos@isecauditors.com
jolmos@...
Upcoming SlideShare
Loading in...5
×

Seguridad de Código basada en Tecnología Vírica. No cON Name 2006

102

Published on

Presentación ofrecida en el Congreso No cON Name 2006 celebrado en Palma de Mallorca, dónde se plantea una posible vía de detección y corrección de problemas de seguridad en ejecutables en tiempo de ejecución.

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
102
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
1
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Transcript of "Seguridad de Código basada en Tecnología Vírica. No cON Name 2006"

  1. 1. Seguridad de Código Basada en tecnología vírica Jesús Olmos González Departamento de Auditoría jolmos@isecauditors.com
  2. 2. Esquema 1. Inseguridad de código y medidas actuales. 2. Análisis de la raíz del problema. 3. Regeneración del código máquina. 4. Infecciones y cold-patching. 5. Prueba de concepto.
  3. 3. 1. Inseguridad de código y medias Inseguridad de código y medidas actuales (las medidas actuales no solucionan el problema de raíz)
  4. 4. ● En el cyberespacio todo es código y datos. ● Todo es automatizable. ● El código también es usuario de internet: – Puede navegar. – Puede chatear. – Puede cometer intrusiones. – Pueden entrar de máquina en máquina en busca de una persona o documento concreto. – Son pequeños y rápidos, cambian de forma, se esconden en otros procesos y en otros ficheros, evolucionan. 1. Inseguridad de código y medidas
  5. 5. 1. Inseguridad de código y medidas ● El atacante siempre es código. ● Puede ser código controlado por una persona: – Necesita conexión entre el código y la persona. – Es más detectable. – Tiene la inteligencia de una persona. – Es lento. ● Puede ser código vírico: – Se mueve rápido. – Es directo. – Difícil de detectar. – No es inteligente.
  6. 6. 1. Inseguridad de código y medidas ● La computadora es una máquina de ejecutar código. ● Ni el procesador ni el sistema operativo distinguen si es código bueno, o malo o si ha sido introducido por el usuario o por un atacante. ● Las computadoras se relacionan entre ellas. ● Muchos datos de unas máquinas son introducidos en la memoria RAM de otras. ● El procesador ejecuta el código que hay en la RAM.
  7. 7. 1. Inseguridad de código y medidas
  8. 8. 1. Inseguridad de código y medidas
  9. 9. 1. Inseguridad de código y medidas char busca[MAXIMA_BUSQUEDA]; char log_busquedas[LOG_NAME_SIZE]; ● El orden no determina la seguridad, el compilador puede variarlo, además se pueden dar overflows en sentido inverso. ● ¿Se puede asaltar otra variable? ● Atacante: – Controlas el código, o el código te controla? ● Where + What = Dentro!
  10. 10. 1. Inseguridad de código y medidas char busca[MAXIMA_BUSQUEDA]; void (*fptr)(void); ● Redirección de la ejecución. ● Where + What = Dentro! ● Defensor: – ¿El código controla al usuario o el usuario controla al código?
  11. 11. 1. Inseguridad de código y medidas ¿Porqué se puede saltar de una variable a otra? PAX-GR etc... ADMIN GCC Pues porque no hay variables. Si se piensa en C, no se ve la realidad se necesita pensar en asm. Habría que cambiar la estructura ELF / PE y cambiar los compiladores :( AUDITOR Podemos tapar un poco el problema poniendo trampas y dificultades por la memoria :) Tapar el problema puede evitar muchos ataques, pero siempre deja brechas. Mientras los compiladores no lo hagan mejor sugiero... REGENERAR ELREGENERAR EL CÓDIGO BINARIOCÓDIGO BINARIO
  12. 12. 1. Inseguridad de código y medidas ● El firewall tapa el problema: – Suelen ser efectivos en su labor de tapa. – Más código que interactua con los paquetes. – Más vulnerabilidades. – Si cae el firewall, o se encuentra otra vía, la vulnerabilidad queda expuesta.
  13. 13. 1. Inseguridad de código y medidas ● El firewall determina quien tiene acceso a que servicio
  14. 14. 1. Inseguridad de código y medidas ● El firewall de aplicación web tapa los fallos de código: – Siempre hay fallos de código, pues se necesita una barrera extra. – El protocolo HTTP da mucho juego, hay muchas evasiones posibles. – Más código, más vulnerabilidades. – Sería más efectivo diseñar un buen corrector de código web. – Puede haber vías de ataque que no pasen por el firewall.
  15. 15. 1. Inseguridad de código y medidas ● El sistema antivirus, frena lo que ya ha entrado: – Puede dificultar la subida de herramientas por parte de los atacantes. – No corrige la vulnerabilidad, por donde entra el código. – El código ya esta dentro! – Analizar el contenido de los ficheros es lento, hay ventanas de tiempo. – Hookeando syscalls, puede ser detectado después de que haya realizado un payload malévolo. – Puede mutar para evadir ficheros cebo.
  16. 16. 1. Inseguridad de código y medidas ● El sistema IDS no corrige el problema: – Es importante la detección. – Si se detecta, ya nos ha entrado el código. – Los logs muchas veces no se miran. – Sobreinformación.
  17. 17. 1. Inseguridad de código y medidas ● El sistema IPS puede tapar las vulnerabilidades: – Puede impedir el ataque, tomando medidas como parar servicio o máquina, banear IP atacante etc.. – El servicio sigue siendo vulnerable. – Se puede evadir las firmas del IPS.
  18. 18. 1. Inseguridad de código y medidas ● La teoría X^W no impide el bof: – Es lógico y necesario. – Dificulta la explotación en algunos casos. – El Buffer Overflow se sigue produciendo. – Se continua teniendo control del retorno de pila. – Se continua teniendo control de la cabecera del siguiente chunk en heap. – Se puede saltar a librerias mapeadas. – ¡Se pueden invadir otras variables!
  19. 19. 1. Inseguridad de código y medidas ● Direcciones aleatorias: – Afecta al rendimiento. – Dificulta la explotación en algunos casos. – El Buffer Overflow se sigue produciendo. – Se continua teniendo control del retorno de pila. – Se continua teniendo control de la cabecera del siguiente chunk en heap. – No es viable averiguar la dirección de librerías donde saltar. – ¡¡Se pueden invadir otras variables!!
  20. 20. 1. Inseguridad de código y medidas ● Canary values: – Afectan al rendimiento. – Normalmente no es viable realizar bruteforce del canary. – El nivel de entropía puede ser insuficiente. – Pueden impedir tomar control del retorno de pila. – Pueden impedir tomar control de la cabecera del chunk siguiente en heap. – ¡¡Se pueden invadir otras variables!!!
  21. 21. 1. Inseguridad de código y medidas ● Correctores de código fuente: – Analizan la raíz del problema. – El código fuente tiene la información suficiente como para solucionar los problemas. – Con el código cerrado no se puede hacer nada. – El corrector de código se debería usar más incluso que el corrector ortográfico. – El compilador puede introducir bugs. – Lo ideal sería un corrector de código máquina.
  22. 22. 2. Anaísis de la raíz del problema Análisis de la raíz del problema (que sucede tras la compilación)
  23. 23. 2. Análisis de la raíz del problema ● Antes de la compilación se tiene mucha información. tipo descripcion[cantidad]; sizeof(descripcion) = tipo * cantidad; ● Los sizeof() se calculan en tiempo de compilación ya que despues es imposible saber los tamaños ni los tipos.
  24. 24. 2. Análisis de la raíz del problema ● Después de la compilación no tenemos cantidades, ni tipos, ni mucho menos descripciones. ● Todo son bytes. ● Hay bytes que indican como interpretar los bytes.
  25. 25. 2. Análisis de la raíz del problema ● No hay límites. ● El código dicta como utilizar los datos. ● ¿Cómo diferenciar si el código lo hace mal? ● Se deberían poder marcar límites inquebrantables entre variables. – No se darían los bofs. – No se permitiría escribir en memoria un carácter utilizado para marcar límites. ● ¿¿Pero, cómo identificar los límites en un precompilado???
  26. 26. 2. Análisis de la raíz del problema ● Imaginemos la pila en tiempo de ejecución. ● ¿Como podemos ver que el strcpy puede invadir otras variables?
  27. 27. 2. Análisis de la raíz del problema ● La pila tendrá 3 estados: – Inicial. – Tras ejecutarse fptr = (void(*)(void))main; – Tras ejecutarse el strcpy. ● Intentemos localizar los límites.
  28. 28. 2. Análisis de la raíz del problema
  29. 29. 2. Análisis de la raíz del problema ● Mirando los datos no se pueden ver los límites. ● Es posible imaginarlos con técnicas de reversing. ● Se necesita ver como el código interactua con los datos para aproximar los límites. ● Las protecciones de pila, heap, randomización, etc. dan rodeos. ● Es posible realizar un algoritmo que aproxime los límites y corrija el binario para que no los cruce.
  30. 30. 2. Análisis de la raíz del problema
  31. 31. 2. Análisis de la raíz del problema CONOCIDO APROXIMADO 0xffffffec(%ebp) <=0xec bytes(%esp) 4bytes ● Las tablas varían según se va ejecutando el código. 0x04(%esp) 4bytes
  32. 32. 2. Análisis de la raíz del problema ● Para corregir una línea de código se tiene en cuenta el estado de las tablas en ese momento. ● No sólo se pueden sacar las aproximaciones de las llamadas externas, también de lecturas, escrituras, posicionamiento (lea) ... ● Las aproximaciones son límites reales hasta que se identifiquen mejor. ● En heap se pueden localizar los inicios y tamaños de variables al 100% pudiendo así erradicar el heap overflow/underflow. ●
  33. 33. 2. Análisis de la raíz del problema
  34. 34. 2. Análisis de la raíz del problema CONOCIDO APROXIMADO 0xfffffffc(%ebp) 4 bytes ● Las tablas varían según se va ejecutando el código. 0xfffffff8(%ebp) 4 bytes 0xfffffff4(%ebp) 4 bytes [0xfffffffc(%ebp) ] 0x3e8 bytes [0xfffffff8(%ebp) ] 0xa bytes [0xfffffff4(%ebp) ] 0x29a bytes
  35. 35. 2. Análisis de la raíz del problema ● Hay que tener en cuenta que el código puede ir reservando más memoria o destruyendo las variables ● Hay que ir actualizando la tabla de variables conocidas. ● No sólo se puede alvergar memoria dinámica con mallocs, también existen otras maneras como mmap. ● Se puede utilizar la memoria dinámica directamente sin utilizar implementaciones que lo hagan por ti. ●
  36. 36. 2. Análisis de la raíz del problema ● Las aproximaciones también son de gran ayuda.
  37. 37. 3. Regeneración del código máquina Regeneración del código máquina. (como corregir el código, conociendo los límites reales y aproximados)
  38. 38. 3. Regeneración del código máquina
  39. 39. 3. Regeneración del código máquina ● El salto que realiza el parche es relativo. ● Sin optimización habría realizado un strcpy, el cual se debería regenerar a strncpy ● Para mayor seguridad el registro utilizado por el parche podría guardarse en pila y al final restaurarse (push / pop). ● Normalmente el número de interaciones se debe a una condición. El parcheo es similar al anterior. ● En muchos casos una variable marca el número de iteraciones. El parche aquí es simple, basta con modificar el valor. ●
  40. 40. 3. Regeneración del código máquina ● ¿Cómo distinguir que es cada llamada externa?● strcpy _start PLT (XR) GOT (RW) main strcpy libc
  41. 41. 3. Regeneración del código máquina
  42. 42. 3. Regeneración del código máquina ● En win32 se puede buscar directamente el nombre de la api en la AddressOfNames de la import table, y conseguir en la AddressOfOrdinals la dirección. ● Se podría buscar en las export-tables de las dlls cargadas e indexar las direcciones externas. ● Las principal diferencia es el formato del ejecutable, el cual afecta al apartado 4.
  43. 43. 3. Regeneración del código máquina
  44. 44. 3. Regeneración del código máquina ● Los únicos límites detectados serían los de la pila (esp y ebp) en este caso son 100% correctos. ● Se podría modificar simplemente el número de loops a realizar para sanear este código, lo cual no requiere relocatación. ● Las tablas que guardan los límites, debería de haber una por función de forma enlazada. ● Veamos un caso curioso de overflow.
  45. 45. 3. Regeneración del código máquina ● El regenerador, detectaría inc e i de manera que el primer bof no se podría dar.
  46. 46. 3. Regeneración del código máquina ● El analizador de código podría debugar: – En linux PTRACE_SINGLESTEP – No se necesitan estructuras de opcodes para entender el código. – En windows también hay API para realizarlo. – El flujo de código no entraría en todos los casos (se puede forzar) – El código puede necesitar inputs del usuario, ficheros no existentes, etc.
  47. 47. 3. Regeneración del código máquina ● El analizador de código podría NO debugar: – Se podría recorrer el código desde el mapa. – Se podrían aprovechar rutinas de las bin-utils. – Se podrían aprovechar estructuras de opcodes intel. – Se analizaría función por función sin ejecutarla. – Mayor maniobrabilidad.
  48. 48. 4. Infecciones y cold-patching Infecciones y cold-patching (Podemos aprender de los virus)
  49. 49. 4. Infecciones y cold-patching ● Según como se regenere el código, este puede acabar ocupando más. ● Si el código crece, todos los saltos, llamadas y accesos se desplazan. ● Es necesario volver relocatar todo para que siga funcionando. ● Los virus utilizan la infección para inyectar código extra en el código-victima.
  50. 50. 4. Infecciones y cold-patching ● Infección de memoria (hot-patch) – Hay que desproteger el area de código. – El cambio no es persistente. – Se pueden haber ejecutado ya instrucciónes vulnerables. – Es sencillo de realizar. – Linux: ptrace – Win32: CreateRemoteThread WriteProcessMemory
  51. 51. 4. Infecciones y cold-patching ● Infección de disco (cold-patch) – Hay que mantener los alineamientos y distancias relativas. – Es persistente. – El fichero puede estar bloqueado, en este caso se puede realizar el parcheo en un fichero nuevo. – Se puede realizar en un mapa de memoria, para ahorrar accesos continuos a disco. – Los procesos que se esten ejecutando siguen vulnerables hasta que se vuelvan a reiniciar.
  52. 52. 4. Infecciones y cold-patching ● O se combina cold-patch con hot-patch o sólo se realiza cold-patch. ● Se ha de poder regenerar también las librerias. ● Se puede infectar el algoritmo corrector a los ficheros, de forma que se vayan contagiando, hasta que acabe siendo todo seguro de bofs. ● Por ahora la idea es tener una herramienta tal que: – regen.exe -dll user32.dll – ./regen /usr/bin/exim
  53. 53. 4. Infecciones y cold-patching ● Infección mediante overlay: – Sencilla y efectiva aunque drástica. – Copia todo el código al final del fichero e inserta su motor en su lugar. – El motor carga el código corregido del final del fichero a memoria, le da permisos y lo ejecuta. – No es un sistema muy fino. – Necesita abrirse a si mismo.
  54. 54. 4. Infecciones y cold-patching ● Infección mediante alargamiento de .text – Se necesita relocatar definiciones de secciones y segmentos. – Se necesita relocatar los saltos indirectos de la plt. – Se necesita relocatar todo salto y llamada relativa que cruce el parche. – Se necesita relocatar todo salto y llamada absoluta. – Se necesita relocatar accesos a variables. – El parche es irreversible, se debe crear backup. – No es mala solución aunque la que sigue es mejor.
  55. 55. 4. Infecciones y cold-patching ● Infección mediante creación de .patch – Se crea sección nueva y se registra en shstrtab. – Toda función vulnerable se copia corregida a .patch – Toda llamada a funcion vulnerable se reapunta a .patch – Se necesita relocatar definiciones de secciones y segmentos. – Se necesita relocatar los saltos indirectos de la plt. – Se necesita relocatar todo salto y llamada absoluta. – Se necesita relocatar accesos a variables.
  56. 56. 4. Infecciones y cold-patching ● La creación de un segundo segmento de código no es viable: – Se controla desde kernel. – Las definiciones de segmento no se rigen al 100% por lo que diga el Elf32_Phdr. – Se ha de reubicar todo igualmente. – Se debe crear sección dentro del segmento. – La creación de una nueva seccion .patch dentro del segmento de código es más directo.
  57. 57. 4. Infecciones y cold-patching ● Procedimiento de creación de .patch 1.Remapear con el tamaño del fichero+parche+1 registro de sección. 2.Desplazamiento lógico de offset y virtual de las secciones inferiores a la tabla de secciones. 3.Desplazamiento físico de secciones inferiores. 4.Añadir nueva sección (copiar la .text) y aumentar la e_shnum. 5.Agrandamiento lógico de segmento de texto en el tamaño del parche. 6.Desplazamiento lógico de los segmentos inferiores al de texto (offset y virtual).
  58. 58. 4. Infecciones y cold-patching 7. Desplazamiento lógico de las secciones inferiores a .patch (offset y virtual) 8. Desplazar físicamente lo que haya por debajo del inicio de .patch para que quede espacio para patch. 9. Actualizar e_shoff ya que se ha desplazado fisicamente la tabla de secciones (esta abajo). 10. Parchear :)
  59. 59. 4. Infecciones y cold-patching Registro elf header Tabla de segmentos CODE DATA Tabla de secciones .text .plt .got .patch
  60. 60. 5. Prueba de comcepto Prueba de Concepto (POC)
  61. 61. 4. Infecciones y cold-patching ¿ Preguntas ?¿ Preguntas ?
  62. 62. Autopresentación Jesús Olmos González Internet Security Auditors Departamento de Auditoría jolmos@isecauditors.com jolmos@7a69ezine.org
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×