Compilar fuentes Java online
Pregunta: ¿Qué pasa si necesitamos compilar un fuente Java y no disponemos de un JDK en la workstation donde estamos trabajando?
Respuesta: Buscá en Google.

"En Internet está todo"

Esa fue la frase que solté cuando encontré esta herramienta muy útil, que nos puede sacar de un apuro. Leyendo un libro sobre programación en Java me surgieron dudas y necesité probar un ejemplo rápido. El problema era que no disponía de javac en la máquina donde estaba trabajando, y no tenía ganas de descargar e instalar el JDK.
Buscando en Google encontré este servicio muy útil que nos permite compilar fuentes java: JXXX Compiler Service.


El mismo permite subir archivos fuente y librerías y compilar el código. Si hubo errores durante el proceso de compilación, el servicio devuelve la salida de javac. Lo más interesante que tiene, es que si estamos compilando un applet lo incluirá directamente en la página para probar su funcionamiento.
Aunque el servicio es realmente útil, no lo utilizaría para compilar código propietario ya que estamos enviando nuestro código a terceros y no sabemos que harán con él (además de compilarlo). Pero nos sirve perfectamente para compilar código de prueba y sacarnos de un apuro.

Veamos un ejemplo utilizando el siguiente código:
class A {
}

class B extends A {
}

class C {
}

class test {
public static void main(String[] args) {
A a = new A();
B b = new B();
if (b instanceof A) {
System.out.println("b is an A");
}
else {
System.out.println("b is NOT an A");
}
}
}

Si compilamos el código anterior introduciendo un error, obtenemos lo siguiente:


Luego, corregimos el error y podemos descargar el código fuente Java compilado .class:


Ahora podemos descargar y ejecutar el .class:



Espero que les sirva!
Cómo descifrar un hash MD5 online
Aloe Vera
Este fin de semana me encontré con la necesidad de descifrar un password hasheado con md5. Para ello estuve a punto de utilizar el viejo y querido John the Ripper, pero un segundo antes se me ocurrió buscar alguna herramienta online que haga el trabajo por mí. Motivado por la vagancia, pero también para hacerlo más rápidamente (aprovechando la nube o rainbow tables), encontré buscando en Google este muy buen artículo: How to crack MD5 passwords online, el cual contiene una lista de servicios para reconstruir texto digerido mediante md5.
La mayoría de estos sitios utilizan rainbow tables (grandes tablas de hashes precomputados) con el objetivo de intercambiar tiempo de ejecución por memoria. De esta forma el resultado logrado es muy rápido (si es exitoso). Aunque también algunos utilizan diccionarios.

A continuación dejo la lista de servicios que realizan esta tarea. Les recomiendo que lean el artículo original, ya que el autor incluye una valuación de los mismos (calculada como la cantidad de hashes encontrados de un conjunto de 10):
www.tmto.org
md5.noisette.ch
md5decryption.com
www.c0llision.net
www.netmd5crack.com
www.md5decrypter.com
md5hashcracker.appspot.com
www.hashhack.com
isc.sans.edu
www.md5crack.com
passcracking.com
authsecu.com
md5.rednoize.com
md5.web-max.ca
www.cmd5.com
md5.thekaine.de
www.shell-storm.org
www.md5this.com
www.hashchecker.com
hashcrack.com
md5pass.com
md5pass.info

Cabe destacar que algunos de estos sitios incluyen una herramienta para generar hashes md5. No es recomendable utilizarlas para calcular hashes de nuestras contraseñas, ya que no sabemos si guardan los hashes que calculamos en sus bases de datos, lo cual las vuelve 100% crackeables ;)

Para lograr mi objetivo, tomé el primer servicio de la lista y obtuve el texto hasheado (admin1234) en 3.55 segundos. Lo que más me molestó fue que había probado sin éxito varias contraseñas, entre las que se encontraban 'admin', '1234' y 'admin123' pero no 'admin1234'!



Para finalizar, dejo un excelente artículo que explica cómo utilizar Google como password cracker (a esta altura creo que a Google le descubren más propiedades que al Aloe Vera). La justificación de esta técnica es que a veces los programadores incluyen hashes md5 en la URL (para ver la explicación detallada les recomiendo lean el artículo), lo que transforma a las bases de datos de Google en rainbow tables.


No me deja de sorprender la cantidad de tareas útiles que se pueden realizar utilizando Google, la mayoría de las cuales con propósitos o fines no pensados en un simple buscador. Es aquí donde uno toma conocimiento del verdadero poder y valor de la información.

Espero que les sirva!
Cómo evitar SQL Injection + Prepared Statements en PHP
Luego de escribir la serie de artículos sobre hacking usando SQL Injection, creo que es tiempo de escribir sobre algún método de prevención >=)

Como se puede observar en cualquier bibliografía "decente" sobre SQL Injection, existen básicamente 3 formas de prevenir la inyección, las cuales están muy bien descriptas en el artículo de OWASP SQL Injection Prevention Cheat Sheet. Las opciones son:

- Prepared Statements: Los prepared statements son sentencias pre-compiladas, en las cuales se indica qué parámetros serán ingresados por el usuario. De esta forma podemos indicarle al DBMS cuál es el código a ejecutar y cuáles serán las variables. Esto permite que el motor distinga la sentencia a ejecutar de los datos de entrada y así evitar que el usuario agregue sentencias SQL.
Además de la obvia ventaja de prevenir las inyecciones, éstos permiten mejorar el tiempo de ejecución si la misma sentencia se utiliza más de una vez. Cuando armamos una sentencia SQL, el motor de base de datos analiza, compila y optimiza la forma en que la ejecutará, por ello, si la armamos una sola vez y la ejecutamos varias veces (ya sea utilizando los mismos o diferentes parámetros), el tiempo de ejecución disminuirá considerablemente.

- Stored Procedures: Los stored procedures son más conocidos que los prepared statements entre los desarrolladores debido a su uso extensivo en la programación que interactua con BDs. Se escriben procedimientos en el lenguaje del DBMS (los cuales se almacenan en la base de datos) y desde el código del programa se llaman estos procedimientos con las variables ingresadas por el usuario como los parámetros. De esta forma, el DBMS puede distinguir correctamente variables de código.
Los stored procedures son tan eficaces como los prepared statements, pero hay que tener cuidado de no utilizar los parámetros para armar sentencias, porque estaríamos anulando la defensa.

- Escapar todo dato ingresado por el usuario: probablemente ésta sea la forma más difundida de prevenir SQL Injection y la cual se cita en mucha bibliografía, pero también es la menos recomendada. La idea es que cada vez que el usuario ingrese datos que utilizaremos en una sentencia, escapemos los caracteres especiales (como comillas simples o dobles, barras invertidas "\", o caracteres de comentario "--" o "#", etc) para que el dato sea un solo string y el motor de BD no lo confunda con código a ejecutar. Un ejemplo de función que permite escapar los caracteres especiales, cuando utilizamos php + mysql, es mysql_real_escape_string. Recuerden que esta función no es mágica y se puede evitar como expliqué en el artículo SQL Injection avanzado: consultas simplificadas, file inclusión, ejecución remota y más! en la sección "No puedo usar comillas... no importa!".
Cómo explican en la página de OWASP citada anteriormente, este técnica es frágil y se debe utilizar sólo cuando ya contamos con código inseguro y reescribirlo utilizando prepared statements requeriría un costo inaceptable. Todo desarrollo que se comience de cero debe utilizar prepared statements o stored procedures.


Librería PDO para PHP

Como ya les he mencionado varias veces, soy fan del desarrollo en PHP, por lo cual me expandiré un poco sobre cómo prevenir SQL Injection en este lenguaje. Mi opción favorita para tal caso es utilizar prepared statements, porque mi filosofía es "dejar la base de datos, para almacenar datos", lo cual elimina a los stored procedures como opción. Además de esta forma el código es portable para ser utilizado con distintos motores de base de datos.

En fin, para lograr el objetivo utilizaremos la librería PDO. Como lo describen en la documentación oficial de PHP, PDO (PHP Data Objects) es una interfaz ligera para acceder a bases de datos, la cual permite abstraernos del motor de base de datos, utilizando siempre el mismo conjunto de funciones para acceder a los datos. Esto hace que el código sea portable a cualquiera de las bases de datos soportadas por PDO, entre las que se encuentran las más importantes (MySQL, SQL Server, Postgre, Oracle, SQLite, Informix, Firebird, etc).

PDO no fuerza al usuario a utilizar prepared statements, pero ofrece una serie de funciones para el manejo de los mismos. En ellas se puede utilizar tipado fuerte, lo cual agrega mayor seguridad al especificar el tipo de los datos esperados.


Utilizar los Prepared Statements de PDO

Los prepared statements de PDO son fáciles de utilizar, aunque al principio pueden resultar un poco molestos debido a que se debe escribir un poco más de código al ejecutar consultas.
Veamos una comparación de las funciones que más se utilizan al acceder a la base de datos. Tomo como ejemplo las funciones de MySQL, aunque como saben, PDO funciona con casi cualquier base de datos.

Conexión con la base de datos:
$db = mysq_connect('localhost', 'tester', '123456');
mysql_select_db('test', $db);

Para conectarnos utilizando PDO debemos crear un objeto PDO indicando el tipo de la base de datos a utilizar, el host, el nombre de usuario y el password de la siguiente manera:
$db = new PDO("mysql:host='localhost';dbname='test'", 'tester', '123456');
Consulta a la base de datos:
mysql_query("SELECT * FROM content WHERE id=".$_GET['id'], $db)

Esta es la parte más sensible donde debemos evitar utilizar cualquier parámetro ingresado por el usuario directamente en el string de consulta. Primero preparamos la consulta que debemos ejecutar, indicando con : cuáles son las variables ingresadas por el usuario:
$stmt = $db->prepare("SELECT * FROM content WHERE id= :id");
Luego vinculamos la variable ingresada por el usuario con la utilizada en la consulta, indicamos el tipo, y finalmente ejecutamos la consulta.
$stmt->bindParam(":id", $_GET['id'], PDO::PARAM_INT);
$stmt->execute();
Las 2 llamadas anteriores se pueden unificar en una sola, utilizando un arreglo asociativo en la función execute:
$stmt->execute(array(":id" => $_GET['id']));
La única desventaja de utilizar esta forma de bind es que todos los parámetros son tratados como PDO::PARAM_STR (es decir, strings).
Obtener una fila en un arreglo:
mysql_fetch_array($db);

La forma de obtener filas es muy similar a la llamada anterior, lo que debemos ejecutar es lo siguiente:
$stmt->fetch();
Al igual que mysql_fetch_array, el fetch de PDO obtiene por defecto un arreglo con ambos índices: asociativo y numérico. Si deseamos obtener sólo el arreglo asociativo, podemos utilizar el parámetro PDO::FETCH_ASSOC. En caso de desear uno con índices numéricos el parámetro es PDO::FETCH_NUM:
$stmt->fetch(PDO::FETCH_ASSOC) o $stmt->fetch(PDO::FETCH_NUM);
Además contamos con una función que retorna todas las filas resultantes de la ejecución de la consulta:
$stmt->fetchAll();
Ver errores:
mysql_error($db);

En el caso de prepared statements, los errores se obtienen con errorInfo:
$stmt->errorInfo();
el cual retorna un arreglo que contiene:
0 código de error SQLSTATE
1 código de error
2 mensaje de error

Bueno, con eso cubrimos las funciones básicas de acceso a la base de datos, aunque existen unas cuantas más y las pueden ver en el manual The PDOStatement class.


Conclusión

Prevenir las inyecciones SQL es muy sencillo utilizando prepared statements y es sólo cuestión de ser lo suficientemente responsable (y consciente) a la hora de escribir consultas. Una vez que uno se acostumbra, la escritura del código sale de forma natural y esas "líneas de más" no resultan tan molestas. Recuerden que esto les salvará muchos dolores de cabeza y se sentirán tranquilos de que no los "hackearan" utilizando esta técnica.
Write safely! =)


Referencias

- SQL Injection Prevention Cheat Sheet
- Introduction to PHP PDO
- Prepared statements and stored procedures (Prepared statements and stored procedures)
- PHP manual PDO
- Prepared Statements
Mounting lvm partitions: get into the dark depths of disk storage
Buenas amigos, hoy traigo este artículo desde las oscuras tierras donde moran los sysadmins. Producto de romperme la cabeza para lograr un mayor entendimiento sobre cómo funciona un sistema de archivos. Antes que nada debo excusarme por mi escasa actividad en el blog, últimamente mi tiempo se escapa desarrollando Web (PHP+MySQL+HTML+CSS+JavaScript), mi trabajo en seguridad, estudiando Java para tratar de certificar este año, y lo que me resulta más interesante: incursionando en virtualización de servidores utilizando KVM sobre host CentOS. Varias veces pensé en escribir sobre virtualización en KVM, pero el tema es tan amplio que no sé cómo encararlo. Tal vez debería empezar con mini-artículos sobre temas puntuales, como este que escribo, pero me falta tiempo. Prometo más adelante abordar el tema...


Breve intro

Para experimentar con virtualización tomé la decisión de utilizar CentOS, clon a nivel binario de RedHat, y utilizar KVM en favor de Xen. Para las máquinas virtuales decidí utilizar volúmenes lógicos de LVM como discos virtuales en lugar de archivos de imagen. No voy a explicar en este artículo las ventajas de utilizar volúmenes lógicos así que vamos directo a lo técnico...


Necesito un backup de mi máquina virtual

(Si necesitas un tutorial de LVM no lo vas a encontrar en este artículo, Google it!, hay muchos buenos tutoriales dando vueltas: 1, 2, 3)

Necesito hacer un backup de una máquina virtual cuyo disco es un volumen lógico y no un archivo de imagen. Además necesito mantener la máquina virtual en ejecución. Es decir, hacer un backup online. No hay problema! Una de las ventajas de LVM es que permite tomar snapshots de volúmenes lógicos, una especie de "instantánea" del contenido del volumen.

En la figura superior vemos un esquema de ejemplo de un disco. Una de las particiones se marca como tipo 8e y queda disponible como espacio para volúmenes lógicos. Supongamos que el volumen lógico asignado a la vm se llama "vm01" y pertenece al grupo de volúmenes lógicos "vm_group". Para crear un snapshot del volumen lógico utilizamos "lvcreate":

lvcreate --size 16G --snapshot --name vm01-snapshot /dev/vm_group/vm01

Donde --size indica el tamaño máximo del volumen, --name indica el nombre del volumen lógico correspondiente al snapshot y el último parámetro indica la ruta al dispositivo. El resultado es una copia del volumen en un instante determinado.


Listo el backup!

Eso es todo, aunque... Si el guest posee una base de datos, se debe hacer un lock para que cualquier aplicación en ejecución no escriba en tablas, y además se debe hacer un flush para escribir a disco todos los registros que se encuentran en memoria. Esto es necesario para que la base de datos quede en un estado consistente al momento de tomar el snapshot. Se puede hacer utilizando la herramienta ShellSQL (en CentOS se debe agregar el repositorio RPMforge para instalarla)


Que hago con el backup?

Una vez que tenemos la snapshot seguramente queremos moverla a un servidor de backup. Para esto necesitamos montar la imagen y copiarla al servidor de backup utilizando ftp (quise decir sftp, ver este artículo) o inclusive scp.

En Linux no es posible montar una partición LVM (tipo 8e), sólo es posible montar volúmenes lógicos. Esto es fácil si el volumen lógico posee un sistema de archivos como ext2, ext3 o NTFS. Los problemas surgen cuando el volumen lógico (partición lógica) contiene varias particiones en lugar de un sistema de archivos (las cosas se vuelven algo confusas).

¿Por qué un volumen lógico tendría varias particiones? Simple, si es utilizado por una máquina virtual tiene al menos una partición, ya que todo sistema operativo necesita al menos una o varias particiones para funcionar (/boot, /, /var, /home, etc.)

Si tratamos de montar un volumen lógico particionado obtendremos el siguiente resultado:

hfs: unable to find HFS+ superblock

Significa que mount no encuentra ningún sistema de archivos, ya que para encontrar un sistema de archivos habrá que buscarlo en la tabla de particiones. Recordemos que lo que para el host (el volumen lógico) es una partición, para el guest virtual es un dispositivo. El resultado es que tenemos particiones dentro de una partición lógica :S

Montar una partición que se encuentra dentro de un volumen lógico

Gracias a esta guía pude lograr lo que necesitaba. Primero veamos qué particiones contiene el volumen lógico correspondiente al snapshot:
[root@myserver ~]# fdisk -l -u /dev/vm_group/vm01-snapshot

Disk /dev/vm_group/vm01-snapshot: 10.4 GB, 10485760000 bytes
255 heads, 63 sectors/track, 1274 cylinders, total 20480000 sectors
Units = sectors of 1 * 512 = 512 bytes

Device Boot Start End Blocks Id System
/dev/vm_group/vm01-snapshot1 * 63 20450744 10225341 7 HPFS/NTFS

Se observa que el volumen lógico posee una única partición NTFS. El parámetro -u se utiliza para que las unidades sean en sectores y no en cilindros. Esta partición se puede montar si le indicamos a mount que el volumen lógico vm01 es un loopback device y le pasamos un offset (medido en bytes) al comienzo de la partición deseada.
El sistema de archivos NTFS comienza en el sector 63 (se observa en la captura anterior) y cada sector posee 512 bytes. Por lo tanto el offset es 32256.
Con los siguientes parámetros logramos montar la partición deseada:
[root@myserver ~]# mount -o loop,offset=32256 -t ntfs /dev/vm_group/vm01-snapshot /media/vm01-snapshot/
The disk contains an unclean file system (0, 0).
The file system wasn't safely closed on Windows. Fixing.

Voila! Tenemos la partición montada!
[root@myserver ~]# la /media/vm01-snapshot/
total 1310609
drwxrwxrwx 1 root root 8192 Feb 16 07:39 Archivos de programa
-rwxrwxrwx 1 root root 0 Feb 3 11:32 AUTOEXEC.BAT
-rwxrwxrwx 1 root root 4952 Aug 24 2001 Bootfont.bin
-rwxrwxrwx 1 root root 211 Feb 3 11:29 boot.ini
-rwxrwxrwx 1 root root 0 Feb 3 11:32 CONFIG.SYS
drwxrwxrwx 1 root root 4096 Feb 3 11:37 Documents and Settings
-rwxrwxrwx 1 root root 536399872 Mar 30 12:46 hiberfil.sys
-rwxrwxrwx 1 root root 0 Feb 3 11:32 IO.SYS
-rwxrwxrwx 1 root root 0 Feb 3 11:32 MSDOS.SYS
drwxrwxrwx 1 root root 0 Feb 16 07:37 MSOCache
-rwxrwxrwx 1 root root 47564 Aug 3 2004 NTDETECT.COM
-rwxrwxrwx 1 root root 250640 Aug 3 2004 ntldr
-rwxrwxrwx 1 root root 805306368 Mar 30 12:46 pagefile.sys
drwxrwxrwx 1 root root 4096 Feb 3 11:36 System Volume Information
drwxrwxrwx 1 root root 28672 Feb 16 14:48 WINDOWS

Teniendo acceso a la partición podemos copiar el backup del disco donde deseamos (usando scp o sftp).

Espero que les sirva!