Curso de programación de virus - Infección bajo Linux I

15 - Infección bajo Linux I

[editar]
Curso gratis creado por Wintermute.
22 de Febrero de 2006
Disclaimer y demás

No se iba a librar el sistema operativo Linux de que le metamos nuestras zarpas, ¿verdad? Es curioso, porque mucha gente tiene esa opinión de "¡Ja! ¡Linux jamás puede ser infectado, no te esfuerces!", frase tras la cual vuelven a sentirse seguros en sus Linux sin preocuparse siquiera por averiguar si esto es cierto. Lamentablemente este es el tipo de actitud que le lleva a uno a confiarse y no ver los agujeros de seguridad que pueden traer problemas. Nunca hay que decir "jamás", siempre alguien encontrará una manera; y la actitud correcta consiste en localizar esos agujeros y taparlos para construir un sistema operativo más robusto y fiable, o algún día el usuario de Linux se encontrará con una infección a la que no sabrá hacer frente. Tapar agujeros de seguridad es algo que no podemos hacer con Windows, pues solo ellos pueden repasar todos los boquetes que tiene; y no lo va a hacer porque se lo digamos. Sin embargo Linux es un sistema operativo que todos pueden ayudar a construir, con lo que programar virus que utilicen posibles huecos de seguridad puede convertirse en una tarea loable al advertir de problemas que puedan existir en este sistema.

Tampoco nos engañemos; por suerte no es sencilla una infección masiva en Linux, puesto que los sistemas de privilegios en acceso a ficheros y demás unido a la costumbre de compilar uno mismo el código, evitan de forma razonablemente buena esta posibilidad. Los usuarios de Linux no se envían ejecutables attacheados en emails cuyo texto diga cosas como "enanito si, pero con unos coj...", email ante el cual picaron unos cuantos usuarios de Windows cuando salió el Hybris de Vecna. Pero precisamente creo que el hecho de esta dificultad es lo que llama la atención en Linux e impulsa a uno a intentar atacarlo, ¿cómo programar virus para un sistema operativo supuestamente tan seguro e inexpugnable? Pues bien, más o menos es acerca de lo que vamos a hablar en esta entrega del curso de programación de virus.

Un último detalle; como dije en la entrega sobre infección en Win32, la programación de virus es algo de por sí interesante y que no necesita de que putees a nadie con un virus que hayas programado. El virus se reproduce igual en tu disco duro que en el de otros, y si lo que te interesa es ese rango de cosas que engloba la vida artificial, analizar un sistema operativo para sacar sus agujeritos o buscar fallos de seguridad... ¿qué diferencia hay si no lo sueltas?. Distribuir un virus no tiene ningún sentido, sólo conseguirás joder a pobres usuarios que, aunque tu virus no tenga código destructivo, temerán que si lo tenga... y que puede que en respuesta formateen su disco duro o algo peor, que equivaldría a que tu código tuviera código destructivo.

No me puedo hacer responsable de lo que hagáis con esta información, pero por lo menos os doy un poquito la chapa ;-), aunque, como digo, dudo que alguien a quien realmente le interesa el tema y está haciendo un esfuerzo importante para aprender e investigar, le quede tiempo para la estupidez de soltar el virus... el placer, está en programarlos.

Introducción a Linux

Estrategias de aproximación a Linux

Lo primero que vamos a tener en cuenta es que Linux es sin duda un sistema operativo mucho mejor protegido que Windows. Vamos a estar limitados a los permisos del usuario que ejecute el fichero infectado, y tendremos que actuar en consecuencia. Una de las formas de plantear virus para Linux será la de conseguir estos privilegios de root, con lo que podremos hacer lo que nos venga en gana. No es tan fácil, de todos modos, y podemos citar las siguientes estrategias:

- Solución: Algoritmo del avestruz. Consistente en no hacer nada. Es decir, nosotros infectamos revisando los permisos de los ficheros, o ni siquiera los revisamos y cuando la escritura falla actuamos en consecuencia no infectando el fichero. Esta parece en principio la peor solución, dado que la acción reproductora del virus está muy limitada, aunque tampoco se puede decir que sea una mala solución; la esperanza estaría entonces en que sea ejecutado por el superusuario y entonces aprovechar para instalarse en todos los lugares posibles del sistema, especialmente en sbin/init :-)

-Solución: Uso de exploits. Consistiría en buscar alguna falla del sistema para poder hacerse superusuario y poder infectar sin problemas. El defecto de esta solución es evidente; Linux se revisa constantemente, y los nuevos kernels o versiones del programa afectado llevarían ese fallo parcheado, con lo que el virus perdería su efectividad. Se trata, más bien, de una solución para sistemas Windows ;)

-Solución: Windows/VMWare. Dado que Linux está tan protegido, se puede uno aprovechar del hecho de que la mayor parte de la gente que tiene Linux instalado utiliza también Windows (aunque le cueste admitirlo xD). La cuestión es que un infector multiplataforma para Windows y Linux podría leer diréctamente la tabla de particiones del disco o discos duros, localizar la partición Linux y a través de Windows, momento en que el sistema de ficheros de Linux no está protegido, infectar leyendo las propias tablas de i-nodes los ficheros ELF, de cabeza a por el sbin/init. Con este método, aunque es un tanto difícil y pesado de llevar a cabo (manejar a mano los ficheros con nuestras propias funciones puede ser un tanto tedioso), tenemos la inmensa ventaja de que infectando algún proceso importante podríamos posteriormente meternos donde nos de la gana, y que se podrían realizar ataques a través de aplicaciones tan populares como VmWare (que puede ser fácilmente detectado dado que para su funcionamiento utiliza un ring que no es ni 0 ni 3 en la máquina virtual, lo cual se puede detectar en la terminación de los descriptores de segmento).

- Solución: Infección de RPMs. Una de esas cosas sensibles y con escasa seguridad para un ataque de virus son los ficheros de Redhat Packet Manager o RPM; cada vez es más usual distribuir programas en este formato. Pero este formato tiene unas características muy particulares, que unidas al hecho de que suele ejecutarse como root su instalación, lo muestran como otra forma de acceder a Linux a través de virus informáticos.

Atacar al SO Linux

En este punto encontramos pocas salidas; siempre puede encontrarse un exploit en el kernel de Linux que nos dé privilegios de ring0 en el procesador con lo que podamos hacer lo que queremos, pero ese exploit sabemos que será corregido, con lo que el virus perderá en poco tiempo su funcionalidad. El sistema de protección de memoria bajo Linux está muy bien desarrollado, y no hay forma en circunstancias normales de salir del modo usuario de ejecución (ring3) para hacer en superusuario lo que nos venga en gana. En Windows sí se hace, pero también es cierto que nadie corrige bugs en Windows...

Bajo Linux, tenemos una división de memoria que sitúa 3/4 partes de las direcciones virtuales de memoria (00000000h a C0000000h) para procesos de usuario, y 1/4 para el kernel (0C0000000h a 0FFFFFFFFh) El anillo de ejecución del procesador (hay dos, el ring0 o superusuario y ring3 o usuario) se ve fácilmente en el descriptor de segmento al que se refiere la parte a la que se intenta acceder. Sus dos últimos bits indican el RPL o modo de ejecución, estando los dos activados para ring3 y ninguno para ring0. Bajo Linux precisamente se inicializan cuatro segmentos básicos, para código y datos en kernel y procesos de usuario; 010h y 018h son código y datos del kernel respectivamente, y 23h y 2bh para código y datos de procesos de usuario.

010h -> Kernel -> 00010000b

018h -> Kernel-> 00011000b

023h -> Usuario -> 00100011b

02bh -> Usuario -> 00101011b

La pregunta entonces, dado que no podemos acceder a la zona reservada a kernel más allá de la dirección de memoria C0000000h, es, ¿cómo entonces podemos acceder a funciones de manejo de disco, etc, si normalmente estamos en ring3?. Para eso se implementan las interrupciones, y en particular la básica de la API de Linux, la int 080h (también hay otro sistema equivalente para compatibilidad con otros Unix como Solaris que utiliza Lcalls, aunque no tenemos nada que hacer aquí)

Al llamar a la int 080h, el procesador consulta una tabla de vectores de interrupción, saltando a la dirección indicada para esta interrupción y pasando automáticamente a ring 0. Por lo tanto podemos hacer esta llamada, pero no podemos modificar ni la indicación del lugar al que salta, ni aquello que hay dónde salta; así pues lo que nos proporcionará la int 080h es la API básica del sistema operativo, que puede ir desde la modificación de ficheros al manejo de sockets. De hecho, todo el tiempo cuando programemos virus, utilizaremos esta función 080h para utilizar la API de Linux.

Como conclusión entonces, la que ofrecí antes; que la única forma de atacar esto es buscar algún exploit, lo cual no es un trabajo sencillo y que tiene el gran inconveniente de que va a servir de poco cuando se saque un parche.

Utilizando la API: Buscando archivos

La forma de llamar a la API del sistema y en particular la que vamos a utilizar, va a ser muy parecida a lo que hacíamos en Ms-Dos. Vamos a poner en AL el valor de la función a la que queremos llamar, y en el resto de registros (ordenados como eax-ebx-ecx-edx-etc) diversos parámetros de nuestra llamada a función. Veamos una mini-lista de funciones que se pueden utilizar con la int80h (ojo, hay muuuuuchas más, esto es solo orientativo; se pueden encontrar listas completas en linuxassembly.org):

|| 1 || sys_exit || Salir del programa en ejecución ||
|| 2 || sys_fork || Hacer un "fork" del proceso (esto significa, "dividirlo" en dos procesos y programar de modo que cada uno siga una línea de ejecución) ||
|| 3 || sys_read || Lectura de un fichero/dispositivo ||
|| 4 || sys_write || Escritura en un fichero/dispositivo ||
|| 11 || sys_execve || Ejecución de un programa ||
|| 21 || sys_mount || Montar un sistema de ficheros ||
|| ... || sys_... || ... ||

Ahora veamos la primera función que nos va a interesar; es la función Open. Tras haber sacado el Delta Offset como hacíamos en Windows (eso es algo que traspasa fronteras y sistemas operativos xD), podríamos hacer algo como esto:

mov eax,05h lea ebx,[diractual+ebp] xor ecx,ecx xor edx,edx int 080h diractual: db '.',0

Vale, ¿qué significa esto? Pues nada más y nada menos que una llamada a "Open", que realizamos sobre el directorio actual (el '.' en diractual). En Linux la forma de hacer el FindFirst/FindNext que hacíamos en Windows es bastante diferente a como lo hacíamos en Windows; tendremos que abrir el directorio para luego ir leyendo sus contenidos con la API ReadDir. Curiosamente esta es de las partes más difíciles cuando uno se pone desde a cero para escribir un virus en Linux, pues veremos que aunque Linux esté muy documentado, en algunos casos la documentación no es correcta (y que nadie se asuste si digo que más de una vez al hacer cosas no habrá más remedio que leerse los fuentes del kernel para ver cómo se hace).

El caso es que lo siguiente que tenemos que hacer es llamar a la órden ReadDir, que nos va a leer una entrada de ese directorio que acabamos de abrir. Una forma de hacerlo es lo siguiente:

mov eax, 059h ; readdir lea ecx, [buffer + ebp] int 080h or ax,ax jz fallo

El parámetro en ECX va a apuntar a un buffer de un tamaño 10Ah, que es la estructura de fichero que nos va a devolver esta llamada. Así, en esta estructura tendremos diversos datos sobre el ejecutable (veamos lo que nos dice sobre ella dirent.h en el kernel):

struct dirent {

long d_ino;

off_t d_off;

unsigned short d_reclen;

char d_name[256]; /* We must not include limits.h! */ };

Lo más importante es lo que tenemos en el desplazamiento 0Ah respecto al principio de la estructura; el nombre del fichero (el primer valor es un long, 4 bytes, el segundo es un puntero, 4 bytes, el tercero es un short, 2 bytes). A partir de él, podremos abrirlo, mapearlo en memoria y finalmente realizar nuestro objetivo; infectarlo. El resto de valores son el i-node (d_ino) correspondiente, el offset respecto a la entrada de directorio (d_off) y el tamaño del nombre (d_reclen) presente en d_name.

Apertura y proyección en memoria

Tal y como hicimos en Windows, en Linux vamos a aprovechar el hecho de que se nos permite mapear ficheros en memoria. Es decir, que en lugar de utilizar un puntero para leer y escribir sobre él, podemos proyectarlo sobre una zona de memoria y escribir sobre ella como si lo hiciéramos en el fichero. Después, al cerrarlo, los cambios que hayamos realizado se guardarán.

mov eax, 5 lea ebx, [buffer + 0Ah + ebp] mov ecx, 2 xor edx, edx int 080h

Parte de esto es muy comprensible; sí, 05h es una función que ya conocemos, la de apertura del fichero. Ebx sin duda está apuntando al nombre del fichero, necesario para abrirlo, mientras que ecx tiene como valor un "2". ¿Qué significa esto? Bien, significa que queremos acceder en lectura/escritura. No hará falta comprobar si tenemos acceso al fichero, si no tenemos permiso la llamada fallará y buscaremos otro. El parámetro EDX en esta llamada hace referencia a un "modo" en caso de que el fichero no exista y lo estemos creando, pero sin duda este no es el caso.

La cosa es que esto nos ha devuelto un "handler" referente al fichero, y nos lo ha devuelto en EAX. Con este descriptor vamos a seguir actuando, y el caso es que lo siguiente que querremos hacer será aumentar el tamaño del fichero para que se adapte a nuestros deseos; si lo vamos a mapear en memoria, querremos poder acceder a él complétamente.

Lo siguiente que hagamos al abrir un fichero entonces, será averiguar cual va a ser su longitud, y para esto tenemos una llamada a la función Lseek, la cual tiene como número de función el 13h. Teniendo tras el anterior código el handler o descriptor en eax, hacemos lo siguiente:

mov ebx, eax mov eax, 013h mov ecx, 0h mov edx, 2h int 080h

El tamaño que pongamos en ECX es lo realmente importante, pero va a ir respecto a EDX. ¿Pero en qué sentido? Pues bien, en EDX vamos a indicar una de tres posibilidades, SEEK_SET, SEEK_CUR y SEEK_END (valores decimales 0, 1 y 2). Con la primera opción, ECX indica el número de bytes del fichero de forma absoluta. Con SEEK_CUR, se hace respecto a la posición del puntero de búsqueda más ECX bytes... y por fin, con SEEK_END, será el tamaño del archivo más ECX bytes... así, en esta ocasión vamos a tener 0 en ECX y 2 en EDX, para averiguar el tamaño del fichero (en EAX) y usarlo posteriormente... ojo, que estamos hablando de un virus que infecta "cavity" (luego veremos qué significa), con lo que el tamaño del fichero no va a aumentar.

Bien, ya tenemos el fichero abierto y sabemos su tamaño, ¿qué es lo siguiente? Pues a no ser que seais masocas y querais jugar con punteros, lo mejor es mapear el fichero en memoria. Y para ello vamos a tener que llamar a la syscall mmap, encargada de ello. Si vemos una descripción, es la siguiente:

void *start (dword, preferred memory address or, NULL) size_t length(dword, file size) int prot (dword, PROT_READ/WRITE/EXEC) int flags (dword, MAP_SHARED/PRIVATE/FIXED) int fd (dword, Linux file descriptor) off_t offset

Y lo que diremos aquí será... otiaaaaaaaa que de parámetros, ¿no? ¿Y eso me cabe en los registros? Pues no... pero es que en Linux hay dos formas de llamar a la API del sistema a través de la int80h, dependiendo de la cantidad de parámetros que haya que meterle (suena algo burro pero es así). El caso es que cuando suceda como en este caso, Linux va a suponer que en EBX ponemos un puntero a todos esos parámetros, y los va a sacar de ahí. Lo más sencillo como podéis suponer, es meter esos parámetros en la pila y luego hacer un mov ebx,esp antes de llamar a la función, de manera que no tenemos que gastar espacio en nuestro programa ni nada por el estilo (lo cierto es que utilizar la pila para guardar datos es una maravillosa costumbre que optimiza mucho xD, por algo es lo que siempre se usa para las variables locales en funciones, pero eso es otra historia).

Veamos un ejemplo práctico de cómo hacerlo:

push 0 push ebx ; el handler o file descriptor push 1 ; privado push 3 push eax ; lo que nos devolvió la llamada a la int anterior push 0 ; NULL para que nos indique la dirección donde lo mapea mov ebx, esp mov eax, 0x5a int 080h cmp eax,0xFFFFF000 ; La comprobación de error que hace mmap.c jbe Continuar

Con esto, deberíamos de haber abierto el fichero mapeado en memoria, y en EAX tendríamos la dirección base a partir de la cual ha sido mapeado. La comprobación de error que hago (cmp eax, 0xFFFFFF000h / jbe Continar) es así en el código de mmap.c (la syscall está mal documentada y da problemas a veces al comprobar si existe algún fallo si se sigue el procedimiento "standard").

Vistas estas cosas acerca de la API que vamos a tener que utilizar para infectar (por suerte no vamos a tener que hacer cosas tan terribles como hacíamos en Windows para tener que sacar las direcciones de la API al basarse en llamadas a la int80h), ya podemos empezar a hablar de ejecutables ELF en Linux.
[editar]

110 opiniones

q buena

pues tios les digo q lo aprendan por que por ejemplo si hacen un viruz pero lo hacen con lo que es ensamblador pues asi el tio formatee su pc seguira infectado ademas es recomendado si piensan en conocimiento i en aprender mas de esto


salu2
Duda.

Sacado de: "2 - estructura de computadores"
"dado que los ejemplos nunca sobran, veamos una instrucción como cmp ac, 12. Una vez llegue tras la fase de fetch a la unidad de control, de nuevo se utilizará la alu; en esta ocasión se la indicará mediante señales de control que realice la operación de resta (sub), metiendo por un lado el 12 y por otro el registro ac"

¿por qué hará la operación sub? ¿no sería menos costoso hacer la operacion xor?
¿si el resultado de aplicar dos oprendos a xor da 0 implica que son el mismo número, no?

un saludo!.
Falta pewrsec.

Probe ensamblar el codigo del primer ejemplo pero no encuentra las referencias a las funciones de las apis no existe mas la web donde estan los recursos http://www.oninet.es/usuarios/darknode

me parece muy bueno el curso. Es imposible encontrar algo de ensamlador de 32 bits en español. Entiendo ingles pero ya me produce nauseas tener que leer en ese idioma todo la informacion sobre programacion. Lo unico que le falta es que el autor le de una actualizacion (revision).
Buenisimo.

En realidad me encanto el curso,a demas entendible. Y eso que apenas yo estoy aprendiendo sobre estos temas, pero vaya esta muy claro. Muchas gracias por su ayuda.
Virus.

Pues aqui hay un virus:
abres block de notas y pones lo siguiente:
@echo off
echo... :adcc
msg * adcc=windows_live_447@hotmail.com
goto adcc
se guarda en. Bat
ej: hola. Bat.
1 2 3 4 5 6 7 ... 22 | siguiente >

Cursos gratis relacionados con 'Curso de programación de virus'

La meta de este curso es el aprendizaje de métodos en programación, tanto en teoría... Más »
Completo curso acerca de los virus informáticos, historia, clasificación, protección...
Completo curso de lenguaje ensamblador.
En este glosario, lo primero que se ha de definir es la palabra HACKER ya... Más »
Curso de introducción al Comercio Electrónico.

Autor y licencia de 'Curso de programación de virus'


Curso gratis de Wintermute. Extraido de: CopyLeft
Este contenido ha sido recopilado por el equipo de Wikilearning. Todo el contenido recopilado se ha obtenido respetando y comunicando en nuestro site la licencia de cada fuente.
Wikilearning tiene permiso expreso por escrito de los autores para publicar los contenidos que ha extraído de otras webs, incluyendo su uso comercial.