lunes, 13 de agosto de 2012

Acceso ssh por el puerto 25 - Patch para qmail

Una buena forma de esconder un servicio como SSH, tan propenso a ataques, es dentro de otro servicio que típicamente esta también abierto como es el SMTP/25. Vamos a ver como hacer un patch de qmail para incluir el SSH en dicho servicio mediante un comando.

La idea viene de la forma de iniciar una conexión SSL a través del puerto 25 (conocido como STARTTLS). Cuando nos conectamos al puerto 25, para iniciar la conexión TLS, debemos enviar el comando STARTTLS en texto claro: A partir de entonces la conexión es cifrada.

Podemos definir un comando arbitrario, por ejemplo STARTSSH, para que a partir de entonces tome el control el daemon sshd para transformar la conexión en el puerto 25 en una conexión SSH.
Para añadir el comando deberemos hacer muy pocos cambios en el fichero qmail-smtpd.c:

--- netqmail-1.06/qmail-smtpd.c 2010-09-07 14:14:31.773951293 +0200
+++ netqmail-1.06-ssh/qmail-smtpd.c 2012-05-24 22:55:10.029940202 +0200
@@ -37,6 +37,8 @@
 #include "wait.h"
 #include "fd.h"
 
+#include "startssh.h"
+
 /* start chkuser code */
 #include "chkuser.h"
 /* end chkuser code */
@@ -330,6 +332,21 @@
   if (!stralloc_0(&rcptto)) die_nomem();
   out("250 ok\r\n");
 }
+
+void smtp_startssh(arg) char *arg; {
+  unsigned long long_fd;
+  int fd;
+  char *fdstr;
+  //out("220 2.0.0 Ready to start SSH\r\n");
+  flush();
+
+  if (!startssh())
+    die_syserr();
+
+  /* reset SMTP state */
+  seenmail = 0;
+}
+
 void smtp_starttls(arg) char *arg; {
   unsigned long long_fd;
   int fd;
@@ -705,6 +722,7 @@
 , { "noop", err_noop, flush }
 , { "vrfy", err_vrfy, flush }
 , { "starttls", smtp_starttls, flush }
+, { "startssh", smtp_startssh, flush }
 , { 0, err_unimpl, flush }
 } ;

A continuación deberemos añadir la función startssh() que “spawneará” un sshd en modo inetd. En lugar de usar un socket usará stdin y stdout para comunicarse con el cliente (la parte de red ya esta establecida por el qmail). El fichero startssh.c sería:

#include "scan.h"
#include "env.h"
#include <unistd.h>

int startssh(void)
{
  unsigned long fd;
  char *fdstr;
  char *args[] = {"/usr/bin/sudo", "/usr/sbin/sshd", "-i", (char *) 0 };

  return execv("/usr/bin/sudo", args);
}

También deberemos añadir startssh.h para la definición de la función a incluir en el qmail-smtpd.c:
int startssh(void);
Deberemos añadir un sudo sin password para ejecutar el sshd, ya que qmail no se ejecura como root:
vpopmail ALL=(ALL)       NOPASSWD: /usr/sbin/sshd

El servicio de daemontools sería como el siguiente:

#!/bin/sh

. /var/qmail/ssl/env

QMAILQUEUE="/var/qmail/bin/qmail-scanner-queue.pl" export QMAILQUEUE
MAXSMTPD=`cat /var/qmail/control/concurrencyincoming`
LOCAL=`head -1 /var/qmail/control/me`
QMAILDUID=`id -u qmaild`
NOFILESGID=`id -g qmaild`

if [ -z "$QMAILDUID" -o -z "$NOFILESGID" -o -z "$MAXSMTPD" -o -z "$LOCAL" ]; then
    echo QMAILDUID, NOFILESGID, MAXSMTPD, or LOCAL is unset in
    echo /var/qmail/supervise/qmail-smtpd/run
    exit 1
fi

if [ ! -f /var/qmail/control/rcpthosts ]; then
    echo "No /var/qmail/control/rcpthosts!"
    echo "Refusing to start SMTP listener because it'll create an open relay"
    exit 1
fi

/usr/local/bin/softlimit -m 100000000 \
    /usr/local/bin/sslserver -e -n -v -R -l "$LOCAL" -x /etc/tcp.smtp.cdb -c "$MAXSMTPD" \
         -u 89 -g 89 0 25 /usr/local/bin/rblsmtpd -b -rsbl.spamhaus.org \
         /usr/local/src/netqmail-1.06-ssh/qmail-smtpd mail.systemadmin.es /home/vpopmail/bin/vchkpw /var/qmail/bin/qmail-smtp-auth-wrapper.sh 2>&1 7>&1

Podemos probar de ejecutarlo a mano para ver que funciona:

# /service/qmail-smtpd-ssh/run &
[1] 32279
# sslserver: cafile 32288
sslserver: ccafile 32288
sslserver: cadir 32288 /usr/local/ssl/certs
sslserver: cert 32288 /var/qmail/ssl/mail.systemadmin.es.crt
sslserver: key 32288 /var/qmail/ssl/mail.systemadmin.es.key
sslserver: param 32288 /var/qmail/ssl/dhparam 1024
sslserver: status: 0/20

#
# telnet localhost 25
Trying 127.0.0.1...
sslserver: status: 1/20
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
sslserver: pid 32291 from 127.0.0.1
sslserver: ok 32291 mail.systemadmin.es:127.0.0.1:25 localhost:127.0.0.1::54007
220 mail.systemadmin.es ESMTP
startssh
SSH-2.0-OpenSSH_4.3

sslserver: end 32290 status 0
sslserver: status: 0/20
Protocol mismatch.
Connection closed by foreign host.
 
Una vez habilitado el servicio desde un cliente cualquiera veríamos:

# telnet localhost 25
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
220 mail.systemadmin.es ESMTP
startssh
SSH-2.0-OpenSSH_4.3
quit
Protocol mismatch.
Connection closed by foreign host.
 
Por lo que ahora deberemos modificar el cliente ssh para que ejecute el comando STARTSSH al iniciar la conexión contra el puerto 25. La modificación es simple, si lo hacemos sin ninguna opción que controle que se añada o no dicho comando especial:

--- sshconnect.orig 2012-06-14 19:18:20.220820357 +0200
+++ sshconnect.c 2012-06-14 19:13:00.930746433 +0200
@@ -415,6 +415,8 @@
 
  debug("Connection established.");
 
+ write(sock,"startssh\n",sizeof("startssh\n"));
+
  /* Set SO_KEEPALIVE if requested. */
  if (want_keepalive &&
      setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on,

Con el cliente modificado ya podemos hacer conexiones en el puerto 25 como si se tratase de únicamente de un SSH:

# ./ssh systemadmin.es -p 25 -l jordi
jordi@systemadmin.es's password: 

Una vez conocido el comando STARTSSH ya no tiene gracia, por lo que idealmente el comando debería ser alguna cadena aleatoria, o por ejemplo para complicarlo más, dependiente de la hora.

Por otro lado, este patch puede ser usado como backdoor únicamente cambiando /usr/sbin/sshd por /bin/bash.

No hay comentarios:

Publicar un comentario

Nota: solo los miembros de este blog pueden publicar comentarios.