Nmap Development mailing list archives

Re: Ncrack postgres + redis modules


From: Fotis Hantzis <ithilgore.ryu.l () gmail com>
Date: Tue, 15 Mar 2016 16:48:00 -0500

Thanks Deirme!
They will be integrated in the upcoming Ncrack 0.5 release.
Cheers,
ithilgore

On Tue, Mar 15, 2016 at 4:44 PM, Evangel Deirme <edeirme () gmail com> wrote:

Hey all,

I'm attaching two modules for ncrack that you might find interesting,
postgres and redis. The postgres module is designed to support MD5
authentication and has been tested for versions 9.3.x and 9.4.x. If you
find any bugs please let me know.

======== Postgres ==============

#include "ncrack.h"
#include "nsock.h"
#include "NcrackOps.h"
#include "Service.h"
#include "modules.h"
#include <openssl/md5.h>

#define PSQL_TIMEOUT 20000
#define PSQL_DIGITS 1
#define PSQL_PACKET_LENGTH 4
#define PSQL_AUTH_TYPE 4
#define PSQL_SALT 4

extern void ncrack_read_handler(nsock_pool nsp, nsock_event nse, void
*mydata);
extern void ncrack_write_handler(nsock_pool nsp, nsock_event nse, void
*mydata);
extern void ncrack_module_end(nsock_pool nsp, void *mydata);
static int psql_loop_read(nsock_pool nsp, Connection *con);
unsigned char charToHexDigit(char c);
enum states { PSQL_INIT, PSQL_AUTH, PSQL_FINI };



/* n is the size of src. dest must have at least n * 2 + 1 allocated
bytes. */
static char *enhex(char *dest, const unsigned char *src, size_t n)
{
    unsigned int i;

    for (i = 0; i < n; i++)
        Snprintf(dest + i * 2, 3, "%02x", src[i]);

    return dest;
}


/* Arguments are assumed to be non-NULL, with the exception of nc and
cnonce,
   which may be garbage only if qop == QOP_NONE. */
static void make_response(char buf[MD5_DIGEST_LENGTH * 2 + 3 + 1],
    const char *username, const char *password, const char *salt)
{
    char HA1_hex[MD5_DIGEST_LENGTH * 2 + 1], HA2_hex[MD5_DIGEST_LENGTH * 2
+ 1];
    unsigned char hashbuf[MD5_DIGEST_LENGTH];
    char finalhash[MD5_DIGEST_LENGTH * 2 + 3];
    MD5_CTX md5;

    /* Calculate MD5(Password + Username) */
    MD5_Init(&md5);
    MD5_Update(&md5, password, strlen(password));
    MD5_Update(&md5, username, strlen(username));
    MD5_Final(hashbuf, &md5);
    enhex(HA1_hex, hashbuf, sizeof(hashbuf));

    /* Calculate response MD5(above + Salt). */
    MD5_Init(&md5);
    MD5_Update(&md5, HA1_hex, strlen(HA1_hex));
    MD5_Update(&md5, salt, strlen(salt));
    MD5_Final(hashbuf, &md5);
    enhex(buf, hashbuf, sizeof(hashbuf));

    /* Add the string md5 at the beggining. */
    strncpy(finalhash,"md5", sizeof("md5"));
    strncat(finalhash, buf, sizeof(finalhash));
    buf[0] = '\0';
    strncpy(buf, finalhash, MD5_DIGEST_LENGTH * 2 + 3);
    buf[MD5_DIGEST_LENGTH * 2 + 3] = '\0';
}

static int
psql_loop_read(nsock_pool nsp, Connection *con, char *psql_code_ret, char
*psql_salt_ret)
{
  int i,j = 0;
  char psql_code[PSQL_DIGITS + 1]; /* 1 char + '\0' */
  char psql_salt[PSQL_SALT + 1]; /* 4 + '\0' */
  char dig[2]; /* temporary digit string */
  char *p;
  int packet_length;
  int authentication_type;

  if (con->inbuf == NULL || con->inbuf->get_len() < PSQL_DIGITS + 1) {
    nsock_read(nsp, con->niod, ncrack_read_handler, PSQL_TIMEOUT, con);
    return -1;
  }

  /* Get the first character */
  p = (char *)con->inbuf->get_dataptr();
  dig[1] = '\0';
  for (i = 0; i < PSQL_DIGITS; i++) {
    psql_code[i] = *p++;
  }
  psql_code[1] = '\0';
  strncpy(psql_code_ret, psql_code, PSQL_DIGITS);

  if (!strncmp(psql_code_ret, "R", PSQL_DIGITS)) {
    /* Read packet length only if it is of type R */

    /* Currently we care only for the last byte.
       The packet length will always be small enough
       to fit in one byte */
    for (i = 0; i < PSQL_PACKET_LENGTH; i++) {
     snprintf(dig, 3, "\n%x", *p++);
     packet_length = (int)strtol(dig, NULL, 16);
    }
    if (con->inbuf->get_len() < packet_length + 1) {
      nsock_read(nsp, con->niod, ncrack_read_handler, PSQL_TIMEOUT, con);
      return -1;
    }

    /* At this point we will need to know the authentication type.
      Possible values are 5 and 0. 5 stands for MD5 and 0 stands for
successful
      authentication. If it is 5 we are at the second stage of the process
PSQL_AUTH
      while if it is 0 we are at the third stage PSQL_FINI.
      This value consists of 4 bytes but only the fourth will have a value
      e.g. 00 00 00 05 for MD5 or 00 00 00 00 for successful
authentication. */
    for (i = 0; i < PSQL_AUTH_TYPE; i++) {
      snprintf(dig, 3, "\n%x", *p++);
      authentication_type = (int)strtol(dig, NULL, 16);
    }


    /* If authentication type is 'MD5 password' (carries Salt) read salt */
    if (authentication_type == 5) {

      for (i = 0; i < 4; i++){
        psql_salt[i] = *p++;
      }
      psql_salt[4] = '\0';
      strncpy(psql_salt_ret, psql_salt, PSQL_SALT);

      return 0;

    }
    else if (authentication_type == 0)
      /* Successful authentication */
      return 1;

  } else if (!strncmp(psql_code_ret, "E", PSQL_DIGITS)) {

    /* Error packet. The login attempt has failed.
      Perhaps we could do some more validation on the packet.
      Currently any kind of packet with E as the first byte will
      be interpreted as a Error package. It is only a matter
      of concerns if the service is not a Postgres service.  */

    return 0;

  }

    /* Malformed packet. Exit safely. */
    return -2;
}

void
ncrack_psql(nsock_pool nsp, Connection *con)
{
  nsock_iod nsi = con->niod;
  Service *serv = con->service;
  const char *hostinfo = serv->HostInfo();

  char packet_length;
  char psql_code[PSQL_DIGITS + 1];
  char psql_salt[PSQL_SALT + 1];
  memset(psql_code, 0, sizeof(psql_code));
  memset(psql_salt, 0, sizeof(psql_salt));

  char response_hex[MD5_DIGEST_LENGTH *2 + 3];
  switch (con->state)
  {
    case PSQL_INIT:

      con->state = PSQL_AUTH;
      delete con->inbuf;
      con->inbuf = NULL;

      if (con->outbuf)
        delete con->outbuf;

      con->outbuf = new Buf();
      packet_length = strlen(con->user) + 7 +
          strlen("\x03user  database postgres application_name psql
client_encoding UTF8  ");

      con->outbuf->snprintf(packet_length,

 "%c%c%c%c%c\x03%c%cuser%c%s%cdatabase%cpostgres%capplication_name%cpsql%cclient_encoding%cUTF8%c%c",
           0,0,0,packet_length,0,0,0,0,con->user,0,0,0,0,0,0,0,0);
      nsock_write(nsp, nsi, ncrack_write_handler, PSQL_TIMEOUT, con,
        (const char *)con->outbuf->get_dataptr(), con->outbuf->get_len());

      break;

    case PSQL_AUTH:

      if (psql_loop_read(nsp, con, psql_code, psql_salt) < 0)
        break;

      if (!strncmp(psql_code, "E", PSQL_DIGITS))
        return ncrack_module_end(nsp, con);

      make_response(response_hex, con->user , con->pass, psql_salt);

      response_hex[MD5_DIGEST_LENGTH * 2 + 3] = '\0';

      con->state = PSQL_FINI;
      delete con->inbuf;
      con->inbuf = NULL;


      if (con->outbuf)
        delete con->outbuf;

      con->outbuf = new Buf();
      packet_length = strlen(response_hex) + 5 + strlen("p");

      /* This packet will not count the last null byte in packet length
byte,
        that is why we remove one from snprintf. */
      con->outbuf->snprintf(packet_length,
            "p%c%c%c%c%s%c",0,0,0,packet_length - 1,response_hex,0
        );

      nsock_write(nsp, nsi, ncrack_write_handler, PSQL_TIMEOUT, con,
        (const char *)con->outbuf->get_dataptr(), con->outbuf->get_len());
      break;

    case PSQL_FINI:

      if (psql_loop_read(nsp, con, psql_code, psql_salt) < 0)
        break;
      else if (psql_loop_read(nsp, con , psql_code, psql_salt) == 1)
        con->auth_success = true;

      con->state = PSQL_INIT;

      delete con->inbuf;
      con->inbuf = NULL;

      return ncrack_module_end(nsp, con);
  }
  /* make sure that ncrack_module_end() is always called last or returned
to
   * have tail recursion or else stack space overflow might occur */
}



============ Redis ================

#include "ncrack.h"
#include "nsock.h"
#include "NcrackOps.h"
#include "Service.h"
#include "modules.h"

#define REDIS_TIMEOUT 20000

extern void ncrack_read_handler(nsock_pool nsp, nsock_event nse, void
*mydata);
extern void ncrack_write_handler(nsock_pool nsp, nsock_event nse, void
*mydata);
extern void ncrack_module_end(nsock_pool nsp, void *mydata);
static int redis_loop_read(nsock_pool nsp, Connection *con);

enum states { REDIS_INIT, REDIS_FINI };


static int
redis_loop_read(nsock_pool nsp, Connection *con)
{
  char *p;
  if (con->inbuf == NULL) {
    nsock_read(nsp, con->niod, ncrack_read_handler, REDIS_TIMEOUT, con);
    return -1;
  }
  p = (char *)con->inbuf->get_dataptr();
  if (!memsearch((const char *)p, "\r\n", con->inbuf->get_len())) {
    nsock_read(nsp, con->niod, ncrack_read_handler, REDIS_TIMEOUT, con);
    return -1;
  }
  if (memsearch((const char *)p, "OK", con->inbuf->get_len()))
    return 0;
  else if (memsearch((const char *)p, "ERR invalid password",
con->inbuf->get_len()))
    return -2;
  return -1;
}

void
ncrack_redis(nsock_pool nsp, Connection *con)
{
  nsock_iod nsi = con->niod;
  Service *serv = con->service;
  const char *hostinfo = serv->HostInfo();
  switch (con->state)
  {
    case REDIS_INIT:

      con->state = REDIS_FINI;
      delete con->inbuf;
      con->inbuf = NULL;

      if (con->outbuf)
        delete con->outbuf;
      con->outbuf = new Buf();
      con->outbuf->snprintf(7 + strlen(con->pass), "AUTH %s\r\n",
con->pass);
      nsock_write(nsp, nsi, ncrack_write_handler, REDIS_TIMEOUT, con,
        (const char *)con->outbuf->get_dataptr(), con->outbuf->get_len());
      break;

    case REDIS_FINI:

      if (redis_loop_read(nsp, con) == -1)
        break;
      else if(redis_loop_read(nsp, con) == 0)
        con->auth_success = true;
      con->state = REDIS_INIT;

      delete con->inbuf;
      con->inbuf = NULL;

      con->force_close = true;

      return ncrack_module_end(nsp, con);
  }
}
================================



Thanks,

Deirme

_______________________________________________
Sent through the dev mailing list
https://nmap.org/mailman/listinfo/dev
Archived at http://seclists.org/nmap-dev/

_______________________________________________
Sent through the dev mailing list
https://nmap.org/mailman/listinfo/dev
Archived at http://seclists.org/nmap-dev/

Current thread: