Creando shells inversas

Hace unos días en la casa de campo me instalaron un router 4G al que no tengo acceso de administrador, para realizar cualquier cambio sobre el dispositvo tengo que llamar a la operadora de internet y que me lo hagan remotamente (incluso un cambio de password del WIFI, IBRED atenta contra la LOPD…). Mi intención era dejar una raspberry pi realizando algunas tareas domóticas y de monitorización sencillas, al parecerme un engorro tener que estar haciendo llamadas para que me abran distintos puertos, me reserven una entrda en el dhcp,… decidí buscar una solución alternativa. Esto funcionaría también estando detrás de un firewall con los puertos capados, o en una red NAT sin forwarding de puertos.

En este post se tratará el tema: levantar un túnel inverso y hacer forward de un puerto local a un puerto remoto a través del protocolo SSH. Para ello, es necesario tener un segundo equipo en el que poder hacer login SSH sin credenciales (con claves RSA). Para los que no sepáis que es un túnel SSH, os recomiendo la entrada que publicqué hace un tiempo sobre ello ( Túnel SSH Raspberry )

El siguiente paso es configurar el demonio SSH de la máquina remota para que permita hacer keepaliving de la conexión (permitir alargar el tiempo máximo de conexión sin intercambiar información). Para ello debemos modificar el archivo

/etc/ssh/sshd_config

Añadir/modificar las siguientes líneas:

TCPKeepAlive yes
ClientAliveInterval 30
ClientAliveCountMax 99999
GatewayPorts yes

Y reniciar el servicio SSH

Una vez tenemos configurado el demonio SSH y haber intercambiado las claves públicas entre los equipos es lo único que hay que hacer es ejecutar:

ssh -f -N -R 10000:localhost:22 @ -p 22

Este comando redigirá el puerto 22 local, al puerto 10000 de la máquina remota. Si quisiéramos hacer login en la Raspberry podríamos hacer un SSH user@equipo_remoto -p 10000, en realidad esto estaría redirigiendo el tráfico a través de un túnel al puerto 22 de la raspberry. Se podría usar con cualquier otro puerto 80,443,…

Como mi idea era dejar la Raspberry encendida sin preocuparme si se reinicia y quiero acceder monté un pequeño script en Bash para que lo ejecutara cron:

#!/bin/bash

REMOTE_PORT="10000"
LOCAL_FORWARDED_PORT="22"
REMOTE_SSH_PORT="22"
REMOTE_HOST="host_remoto"
REMOTE_USER="user"

PID=$(ps -ef | grep "${REMOTE_HOST}" | grep -v grep | awk '{print $2}')

if [ "" == "$PID" ]; then
    echo "[+] Excecuting reverse ssh connection"
    ssh -f -N -R "${REMOTE_PORT}:localhost:${LOCAL_FORWARDED_PORT}" "${REMOTE_USER}@${REMOTE_HOST}" -p "${REMOTE_SSH_PORT}"
else
    echo "[-] The connection was stablished before"
fi

La entrada de crontab debe tener la siguiente forma:

* * * * * [ -x /root/reverse_ssh.sh ] &&  /root/reverse_ssh.sh &> /tmp/reverse_ssh.log

Si vamos al servidor remoto deberíamos poder ver

user@host_remoto:~$ netstat -an | grep -i 10000
tcp        0      0 0.0.0.0:10000           0.0.0.0:*               LISTEN     
tcp6       0      0 :::10000                :::*                    LISTEN     

Si queremos usar el servicio al que estamos haciendo forwarding (en este caso SSH) podríamos abrir una shell contra el puerto 10000:

user@host_remoto:~$ ssh root@127.0.0.1 -p 10000
The authenticity of host '[127.0.0.1]:10000 ([127.0.0.1]:10000)' can't be established.
ECDSA key fingerprint is XX:XX:XX:XX:XX:XX:XX:XX:XX.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[127.0.0.1]:10000' (ECDSA) to the list of known hosts.

The programs included with the Kali GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Kali GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Sat Apr  8 11:25:42 2017 from ::1
root@raspberry:~# 

En este caso veríamos que la shell está funcionando.

WebSockets – Dashboard colaborativa

Desde hace unos años, se han puesto de moda la tecnología WebSocket.
Esta tecnología es una extensión del protocolo HTTP para simular un socket. Comunmente un sócket de comunicación es un recurso que se utiliza para poder transferir datos entre aplicaciones/máquinas. Existen varios tipos de sockets: UDP,TCP,RAW,UNIX,… cada uno se utiliza para una determinada función.

Por ejemplo, UDP se va muy bien para transferir grandes cantidades de datos ya que reduce el overhead introducido por cabeceras, Los sockets TCP se usan para las aplicaciones que no toleran errores en transmisión ya que posee un buen sistema de control de flujo y errores. En ambos casos existe un evidente problema: se necesitan abrir puertos específicos en los firewalls para poder establecer la conexión. Por defecto, el puerto 80/443 que utiliza el servidor de WebSocket suele estar abierto ya que son los puertos estándares de cualquier servidor HTTP.

Diagrama de Websockets

Este diagrama muestra la negociación y uso del protocolo de websockets

Este tipo de socket se utiliza en los navegadores para poder mantener un flujo de comunicación constante por lo facilita la operativa a los programadores web para tener el contenido actualizado en sus páginas. El clásico ejemplo de uso de websockets es construir una sala de chat en la que en tiempo real se reciben y envían mensajes (Hangouts utiliza websockets), las notificaciones de Facebook utilizan websockets.

Ejemplo básico de uso de Webcoskets con node JS
Servidor:

var server = require('websocket').server, http = require('http');

var socket = new server({
    httpServer: http.createServer().listen(1337)
});

socket.on('request', function(request) {
    var connection = request.accept(null, request.origin);

    connection.on('message', function(message) {
        console.log(message.utf8Data);
        connection.sendUTF('pong');
        setTimeout(function() {
            connection.sendUTF('[!] Server Event happended');
        }, 1000);
    });

    connection.on('close', function(connection) {
        console.log('connection closed');
    });
}); 

Cliente:

<div id="content"></div>

<script type="text/javascript">
    var content = document.getElementById('content');
    var socket = new WebSocket('ws://localhost:1337');
    socket.onopen = function () {
        setTimeout(function() {
                socket.send('ping');
        }, 1000);
    };
    socket.onmessage = function (message) {
        content.innerHTML += message.data +'<br />';
    };

    socket.onerror = function (error) {
        console.log('WebSocket error: ' + error);
    };
</script>

Fuente: http://hawkee.com/snippet/16051/

Para los que trabajamos en grupo en determinadas ocasiones necesitamos sincronizar determinadas tareas, como el reinicio y arranque de determinados servidores. Por ello poder avisar a los compañeros de que has iniciado la tarea y que lo reciban en tiempo real es importante, en esto nos ayudan los websockets. Si alguien quiere ver la potencia que ofrece este tipo de tecnología recomiendo ver una dashboard de ejemplo colaborativa que he diseñado para este fin. El código es libre y susceptible de aceptar mejoras:

https://github.com/berni69/ws-dashboard

Limpiando \Windows\Installer

Desde que instalé windows en mi equipo he visto que la carpeta del sistema C:\Windows\Installer crecía y crecía a medida que iba pasando el tiempo,..

Este comportamiento puede que para la mayoría de personas no sea un problema, pero en los equipos con una partición de sistema reducida (discos ssd, hostings vps,..) o en tablets puede llegar a colapsar el equipo.

Tras pasar CCleaner y varias herramientas más de liberación de espacio esa carepta seguia ocupando alrededor de 50GB, según Microsoft, esta carpeta contiene copias de seguridad de librerias y archivos del sistema que usan los instaladores y desinstaladores de las apliaciones del equipo.

Cuando una aplicación se desinstala es posible que no se eliminen correctamente este tipo de archivos, por lo que van quedando restos y la carpeta va creciendo.

Navegando un poco encontré la herramienta patchcleaner que busca archivos huerfanos y los elimina.

Se puede descargar de:

http://download.cnet.com/PatchCleaner/3000-2094_4-76399133.html?part=dl-10218687&subj=dl&tag=button

Con esta aplicación pude eliminar 30Gb de esa carpeta dejando espacio disponible para mis documentos y archivos.

Espero que os sirva de ayuda

Tratando logs con awk

Hoy en el trabajo me he encontrado con una problemática bastante común cuando tratamos de filtrar logs: mucha información que no nos interesa.

Para solventar este problema, habitualmente, la primera aproximación sería con grep y cut pero habitualmente se quedan cortos ya que es complicado usarlos para filtrar por fecha/hora.

En este ejemplo se usará un archivo de log del estilo

2016/06/29 17:10:35.838:Fatal error core dumped
2016/06/29 17:15:35.838:Fatal error core dumped
2016/06/29 17:20:35.838:Fatal error core dumped
2016/06/29 17:30:35.838:Fatal error core dumped

Para poder filtrar por hora, podemos usar awk, en el siguiente script contaremos el numero de líneas que hay en un intervalo de tiempo determinado (por defecto, los últimos 15 min).

#!/bin/awk -f

BEGIN {

if(minutos==""){minutos=15 }
    umbral = minutos *60  # Miramos los ultimos N segundos del log 
    now = strftime("%H:%M:%S",systime())
    counter=0;
    debug=0
 }
{ 
    split($2,chunks,".");
    hour=chunks[1];
    m=split(hour,w,":")
    n=split(now,t,":")
    FIRSTTIME= (t[1]*3600) + (t[2]*60) + t[3]
    SECONDTIME= (w[1]*3600) + (w[2]*60) + w[3]
    DIFFTIME=(FIRSTTIME - SECONDTIME)
    if (debug == 1){
        printf("%s|%s|%s\n",FIRSTTIME,SECONDTIME,DIFFTIME)
    }
    if(DIFFTIME < umbral ){
        counter++;
    }
}
END { print  counter }

Para ejecutarlo bastaría con ejecutar:

cat mylog.log | awk -f script.awk -v minutos=8

Y el resultado sería

echo '2016/06/29 17:10:35.838:Fatal error core dumped
2016/06/29 17:15:35.838:Fatal error core dumped
2016/06/29 17:20:35.838:Fatal error core dumped
2016/06/29 17:30:35.838:Fatal error core dumped' | awk -f /var/www/scripts/countSuperacionesAma.awk -v minutos=8
1

Pues de este modo tan sencillo podemos tratar logs en función del tiempo.

Streaming procesos en php

En determinadas ocasiones he necesitado generar una web que me ejecute un proceso del sistema operativo que tarda bastante. Es bastante frustrante tener que esperar a que el proceso termine para saber como se está ejecutando, si eres tan impaciente como yo, este es tu código.

<?php
header('Content-Type: text/html; charset=utf-8');
header('Cache-Control: no-cache');
ini_set('max_execution_time', 600);
echo '<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';
echo "<style type='text/css'>
 body{
 background:#000;
 color: #7FFF00;
 font-family:'Lucida Console',sans-serif !important;
 font-size: 12px;
 }
 </style></head><body>";
?>

El primer paso es configurar el timeout de la aplicación para que PHP no cierre el proceso, acto seguido indicamos al navegador que esta web no se debe cachear. Y para darle un poco de formato generamos el estilo de una consola de MSDOS.

El siguiente paso es preparar la salida de PHP para que vaya enviando por el socket HTTP la información que se genera sin esperar a que finalice la ejecución del script.

<?php

function disable_ob() {
    // Turn off output buffering
    ini_set('output_buffering', 'off');
    // Turn off PHP output compression
    ini_set('zlib.output_compression', false);
    // Implicitly flush the buffer(s)
    ini_set('implicit_flush', true);
    ob_implicit_flush(true);
	ob_end_flush();
    // Clear, and turn off output buffering
    while (ob_get_level() > 0) {
        // Get the curent level
        $level = ob_get_level();
        // End the buffering
        ob_end_clean();
        // If the current level has not changed, abort
        if (ob_get_level() == $level) break;
    }
    // Disable apache output buffering/compression
    if (function_exists('apache_setenv')) {
        apache_setenv('no-gzip', '1');
        apache_setenv('dont-vary', '1');
    }
}

// tell php to automatically flush after every output
// including lines of output produced by shell commands
disable_ob();
flush();
?>

Para finalizar sólo nos queda ejecutar el comando e ir imprimiendo su salida. En este caso voy a ejecutar un script en python “unbufered” para que el intérprete no se guarde la salida estándar hasta que termina de ejecutar el comando:


$cmd = 'python -u automate.py 2>&1'; 
$pid = popen( $cmd,"r");
 
echo "<body><pre>";
while( !feof( $pid ) )
{
 echo fread($pid, 256);
 flush();
 //ob_flush();
 echo "<script>window.scrollTo(0,99999);</script>";
 usleep(100000);
}
pclose($pid);

Con las líneas:

 echo "<script>window.scrollTo(0,99999);</script>";
 usleep(100000);

Conseguimos el navegador haga scroll hasta la última linea impresa y que PHP no consuma más recursos de los necesarios ya que no es necesario que esté todo el tiempo comprobando la salida estándar del proceso ejecutado.

Resultado:

Console

Matar un proceso en Windows

Esta tarde un compañero me ha pedido si conocía si se podía matar un proceso dependiendo de la RAM que consumía y relanzarlo sin utilizar nada más que no fuera BATCH en Windows XP,… si señores BATCH 🙂

Pues evidentemente le dije que no lo sabía pero que lo investigaría. Y este ha sido el resultado, este script mata cualquier cmd.exe que consuma más de 4000Kb de RAM.

@echo off
set process=cmd.exe
set memLimit=4000
tasklist /FI "IMAGENAME eq %process%" | findstr  %process% > tmp
for /f "tokens=5" %%x in (tmp) do set memUsage=%%x
echo "Memusage %memUsage%"
if %memUsage% gtr %memLimit% goto killme
goto end 
:killme
taskkill /f /FI "IMAGENAME eq %process%"
%process%
:end
echo "fin"
del tmp

Sé que se puede mejorar el uso de archivos temporales pero.. como le dije a mi compñero BATCH no es mi fuerte