Nmap Development mailing list archives
Ncrack postgres + redis modules
From: Evangel Deirme <edeirme () gmail com>
Date: Tue, 15 Mar 2016 23:44:43 +0200
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/
Current thread:
- Ncrack postgres + redis modules Evangel Deirme (Mar 15)
- Re: Ncrack postgres + redis modules Fotis Hantzis (Mar 15)