diff --git a/Makefile.am b/Makefile.am old mode 100644 new mode 100755 index f0f7754..823e753 --- a/Makefile.am +++ b/Makefile.am @@ -19,7 +19,8 @@ SRC = hydra-vnc.c hydra-pcnfs.c hydra-rexec.c hydra-nntp.c hydra-socks5.c \ hydra-oracle.c hydra-vmauthd.c hydra-asterisk.c hydra-firebird.c hydra-afp.c hydra-ncp.c \ hydra-oracle-sid.c hydra-http-proxy.c hydra-http-form.c hydra-irc.c \ hydra-rdp.c hydra-s7-300.c hydra-redis.c hydra-adam6500.c \ - crc32.c d3des.c bfg.c ntlm.c sasl.c hmacmd5.c hydra-mod.c hydra-rtsp.c hydra-time.c hydra-rpcap.c + crc32.c d3des.c bfg.c ntlm.c sasl.c hmacmd5.c hydra-mod.c hydra-rtsp.c hydra-time.c hydra-rpcap.c \ + hydra-radmin2.c OBJ = hydra-vnc.o hydra-pcnfs.o hydra-rexec.o hydra-nntp.o hydra-socks5.o \ hydra-telnet.o hydra-cisco.o hydra-http.o hydra-ftp.o hydra-imap.o \ hydra-pop3.o hydra-smb.o hydra-icq.o hydra-cisco-enable.o hydra-ldap.o \ @@ -30,7 +31,8 @@ OBJ = hydra-vnc.o hydra-pcnfs.o hydra-rexec.o hydra-nntp.o hydra-socks5.o \ hydra-oracle-sid.o hydra-oracle.o hydra-vmauthd.o hydra-asterisk.o hydra-firebird.o hydra-afp.o hydra-ncp.o \ hydra-http-proxy.o hydra-http-form.o hydra-irc.o hydra-redis.o \ hydra-rdp.o hydra-s7-300.c hydra-adam6500.o \ - crc32.o d3des.o bfg.o ntlm.o sasl.o hmacmd5.o hydra-mod.o hydra-rtsp.o hydra-time.o hydra-rpcap.o + crc32.o d3des.o bfg.o ntlm.o sasl.o hmacmd5.o hydra-mod.o hydra-rtsp.o hydra-time.o hydra-rpcap.o \ + hydra-radmin2.o BINS = hydra pw-inspector EXTRA_DIST = README README.arm README.palm CHANGES TODO INSTALL LICENSE \ diff --git a/configure b/configure index bc66cfa..f774b8c 100755 --- a/configure +++ b/configure @@ -246,6 +246,22 @@ if [ "$SSL_IPATH" = "/usr/include" ]; then SSL_IPATH="" fi +echo "Checking for gcrypt (libgcrypt.so) ..." +for i in $LIBDIRS ; do + if [ "X" = "X$GCRYPT_PATH" ]; then + if [ -f "$i/libgcrypt.so" -o -f "$i/libgcrypt.dylib" -o -f "$i/libgcrypt.a" -o -f "$i/libgcrypt.dll.a" -o -f "$i/libgcrypt.la" ]; then + HAVE_GCRYPT="y" + fi + fi +done +if [ -n "$HAVE_GCRYPT" ]; then + echo " ... found" +else + echo " ... gcrypt not found, gcrypt support disabled" +fi + + + echo "Checking for idn (libidn.so) ..." for i in $LIBDIRS ; do if [ "X" = "X$IDN_PATH" ]; then @@ -1073,6 +1089,10 @@ fi if [ -n "$HAVE_ZLIB" ]; then XDEFINES="$XDEFINES -DHAVE_ZLIB" fi +if [ -n "$HAVE_GCRYPT" ]; then + XDEFINES="$XDEFINES -DHAVE_GCRYPT" +fi + OLDPATH="" for i in $SSL_PATH $FIREBIRD_PATH $WORACLE_LIB_PATH $PCRE_PATH $IDN_PATH $CRYPTO_PATH $SSH_PATH $NSL_PATH $SOCKET_PATH $RESOLV_PATH $SAPR3_PATH $POSTGRES_PATH $SVN_PATH $NCP_PATH $CURSES_PATH $ORACLE_PATH $AFP_PATH $MYSQL_PATH; do @@ -1128,6 +1148,9 @@ fi if [ -n "$ORACLE_IPATH" ]; then XIPATHS="$XIPATHS -I$ORACLE_IPATH" fi +if [ -n "$HAVE_GCRYPT" ]; then + XLIBS="$XLIBS -lgcrypt" +fi if [ -n "$HAVE_ZLIB" ]; then XLIBS="$XLIBS -lz" fi diff --git a/hydra-radmin2.c b/hydra-radmin2.c new file mode 100644 index 0000000..cba0431 --- /dev/null +++ b/hydra-radmin2.c @@ -0,0 +1,362 @@ +#include "hydra-mod.h" +#include +#include +#ifdef HAVE_GCRYPT +#include +#endif + +extern char *HYDRA_EXIT; + +//RAdmin 2.x + +struct rmessage { + uint8_t magic; //Indicates version, probably? + uint32_t length; //Total message size of data. + uint32_t checksum; //Checksum from type to end of data. + uint8_t type; //Command type, table below. + unsigned char data[32]; //data to be sent. +}; + +/* + * Usage: sum = checksum(message); + * Function: Returns a 4 byte little endian sum of the messages typecode+data. This data is zero padded for alignment. + * Example message (big endian): + * [01][00000021][0f43d461] sum([1b6e779a f37189bb c1b22982 c80d1f4d 66678ff9 4b10f0ce eabff6e8 f4fb8338 3b] + zeropad(3)]) + * Sum: is 0f43d461 (big endian) + */ +uint32_t checksum(struct rmessage *msg) { + int32_t blen; + uint8_t *stream; + uint32_t sum; + blen = msg->length; //Get the real length. + blen += (4 - (blen % 4)); + + //Allocate a worksapce. + stream = calloc(blen, sizeof(uint8_t)); + memcpy(stream, &msg->type, sizeof(uint8_t)); + memcpy(stream+1, msg->data, blen-1); + + sum = 0; + for(blen -= sizeof(uint32_t); blen > 0; blen -= sizeof(uint32_t)) { + sum += *(uint32_t *)(stream + blen); + } + sum += *(uint32_t *)stream; + + //Free the workspace. + free(stream); + + return sum; +} + +/* + * Usage: challenge_request(message); + * Function: Modifies message to reflect a request for a challenge. Updates the checksum as appropriate. + */ +void challenge_request(struct rmessage *msg) { + msg->magic = 0x01; + msg->length = 0x01; + msg->type = 0x1b; + msg->checksum = checksum(msg); +} + +/* + * Usage: challenge_request(message); + * Function: Modifies message to reflect a response to a challenge. Updates the checksum as appropriate. + */ +void challenge_response(struct rmessage *msg, unsigned char *solution) { + msg->magic = 0x01; + msg->length = 0x21; + msg->type = 0x09; + memcpy(msg->data, solution, 0x20); + msg->checksum = checksum(msg); +} + +/* + * Usage: buffer = message2buffer(message); send(buffer, message->length + 10); free(buffer) + * Function: Allocates a buffer for transmission and fills the buffer with message data such that it is ready to transmit. + */ +//TODO: conver to a sendMessage() function? +char *message2buffer(struct rmessage *msg) { + char *data; + if(msg == NULL) { + hydra_report(stderr, "rmessage is null\n"); + hydra_child_exit(0); + return NULL; + } + + switch(msg->type) { + case 0x1b: //Challenge request + data = calloc (10, sizeof(unsigned char)); + if(data == NULL) { + hydra_report(stderr, "calloc failure\n"); + hydra_child_exit(0); + } + memcpy(data, &msg->magic, sizeof(char)); + *((int32_t *)(data+1)) = htonl(msg->length); + *((int32_t *)(data+5)) = htonl(msg->checksum); + memcpy((data+9), &msg->type, sizeof(char)); + break; + case 0x09: + data = calloc (42, sizeof(unsigned char)); + if(data == NULL) { + hydra_report(stderr, "calloc failure\n"); + hydra_child_exit(0); + } + memcpy(data, &msg->magic, sizeof(char)); + *((int32_t *)(data+1)) = htonl(msg->length); + *((int32_t *)(data+5)) = htonl(msg->checksum); + memcpy((data+9), &msg->type, sizeof(char)); + memcpy((data+10), msg->data, sizeof(char) * 32); + break; + default: + hydra_report(stderr, "unknown rmessage type\n"); + hydra_child_exit(0); + return NULL; + } + return data; +} + +struct rmessage *buffer2message(char *buffer) { + struct rmessage *msg; + msg = calloc(1, sizeof(struct rmessage)); + if(msg == NULL) { + hydra_report(stderr, "calloc failure\n"); + hydra_child_exit(0); + } + + //Start parsing... + msg->magic = buffer[0]; + buffer += sizeof(char); + msg->length = ntohl(*((uint32_t *)(buffer))); + buffer += sizeof(uint32_t); + msg->checksum = ntohl(*((uint32_t *)(buffer))); + buffer += sizeof(uint32_t); + msg->type = buffer[0]; + buffer += sizeof(char); + + //Verify known fields... + if(msg->magic != 0x01) { + hydra_report(stderr, "Bad magic\n"); + hydra_child_exit(0); + return NULL; + } + + switch(msg->type) { + case 0x1b: + if(msg->length != 0x21) { + hydra_report(stderr, "Bad length...%08x\n", msg->length); + hydra_child_exit(0); + return NULL; + } + memcpy(msg->data, buffer, 32); + break; + case 0x0a: + //Win! + case 0x0b: + //Lose! + break; + default: + hydra_report(stderr, "unknown rmessage type"); + hydra_child_exit(0); + return NULL; + } + return msg; +} + + +int32_t start_radmin2(int32_t s, char *ip, int32_t port, unsigned char options, char *miscptr, FILE * fp) { + return 0; +} + +void service_radmin2(char *ip, int32_t sp, unsigned char options, char *miscptr, FILE * fp, int32_t port, char *hostname) { +#ifdef HAVE_GCRYPT + int32_t sock = -1; + int32_t index; + int32_t bytecount; + char *request; + struct rmessage *msg; + int32_t myport = PORT_RADMIN2; + char buffer[42]; + char password[101]; + uint8_t rawkey[16]; + uint8_t *IV = "\xFE\xDC\xBA\x98\x76\x54\x32\x10\xA3\x9D\x4A\x18\xF8\x5B\x4A\x52"; + uint8_t encrypted[32]; + gcry_error_t err; + gcry_cipher_hd_t cipher; + gcry_md_hd_t md; + + if(port != 0) { + myport = port; + } + + gcry_check_version(NULL); + + memset(buffer, 0x00, sizeof(buffer)); + + //Phone the mother ship + hydra_register_socket(sp); + if( memcmp(hydra_get_next_pair(), &HYDRA_EXIT, sizeof(HYDRA_EXIT)) == 0) { + return; + } + + while(1) { + + /* Typical conversation goes as follows... + 0) connect to server + 1) request challenge + 2) receive 32 byte challenge response + 3) send 32 byte challenge solution + 4) receive 1 byte auth success/fail message + */ + // 0) Connect to the server + sock = hydra_connect_tcp(ip, myport); + if(sock < 0) { + hydra_report(stderr, "Error: Child with pid %d terminating, can not connect\n", (int32_t)getpid()); + hydra_child_exit(1); + } + + // 1) request challenge (working) + msg = calloc(1, sizeof(struct rmessage)); + challenge_request(msg); + request = message2buffer(msg); + hydra_send(sock, request, 10, 0); + free(msg); + free(request); + + //2) receive response (working) + index = 0; + while(index < 42) { //We're always expecting back a 42 byte buffer from a challenge request. + switch(hydra_data_ready(sock)) { + case -1: + hydra_report(stderr, "Error: Child with pid %d terminating, receive error\nerror:\t%s\n", (int32_t)getpid(), strerror(errno)); + hydra_child_exit(1); + break; + case 0: + //keep waiting... + break; + default: + bytecount = hydra_recv(sock, buffer+index, 42 - index); + if(bytecount < 0) { + hydra_report(stderr, "Error: Child with pid %d terminating, receive error\nerror:\t%s\n", (int32_t)getpid(), strerror(errno)); + hydra_child_exit(1); + } + index += bytecount; + } + } + + //3) Send challenge solution. + + // Get a password to work with. + memset(password, 0x00, sizeof(password)); + memset(encrypted, 0x00, sizeof(encrypted)); + hydra_get_next_pair(); + strncpy(password, hydra_get_next_password(), sizeof(password)-1); + + //MD5 the password to generate the password key, this is used with twofish below. + err = gcry_md_open(&md, GCRY_MD_MD5, 0); + if(err) { + hydra_report(stderr, "Error: Child with pid %d terminating, gcry_md_open error (%08x)\n%s/%s", (int32_t)getpid(), index, gcry_strsource(err), gcry_strerror(err)); + hydra_child_exit(1); + } + gcry_md_reset(md); + gcry_md_write(md, password, 100); + if(gcry_md_read(md, 0) == NULL) { + hydra_report(stderr, "Error: Child with pid %d terminating, gcry_md_read error (%08x)\n", (int32_t)getpid(), index); + hydra_child_exit(1); + } + memcpy(rawkey, gcry_md_read(md, 0), 16); + gcry_md_close(md); + + //3.a) generate a new message from the buffer + msg = buffer2message(buffer); + + //3.b) encrypt data received using pkey & known IV + err= gcry_cipher_open(&cipher, GCRY_CIPHER_TWOFISH128, GCRY_CIPHER_MODE_CBC, 0); + if(err) { + hydra_report(stderr, "Error: Child with pid %d terminating, gcry_cipher_open error (%08x)\n%s/%s", (int32_t)getpid(), index, gcry_strsource(err), gcry_strerror(err)); + hydra_child_exit(1); + } + + err = gcry_cipher_setiv(cipher, IV, 16); + if(err) { + hydra_report(stderr, "Error: Child with pid %d terminating, gcry_cipher_setiv error (%08x)\n%s/%s", (int32_t)getpid(), index, gcry_strsource(err), gcry_strerror(err)); + hydra_child_exit(1); + } + + err = gcry_cipher_setkey(cipher, rawkey, 16); + if(err) { + hydra_report(stderr, "Error: Child with pid %d terminating, gcry_cipher_setkey error (%08x)\n%s/%s", (int32_t)getpid(), index, gcry_strsource(err), gcry_strerror(err)); + hydra_child_exit(1); + } + + err = gcry_cipher_encrypt(cipher, encrypted, 32, msg->data, 32); + if(err) { + hydra_report(stderr, "Error: Child with pid %d terminating, gcry_cipher_encrypt error (%08x)\n%s/%s", (int32_t)getpid(), index, gcry_strsource(err), gcry_strerror(err)); + hydra_child_exit(1); + } + + gcry_cipher_close(cipher); + + //3.c) half sum - this is the solution to the challenge. + for(index=0; index < 16; index++) { + *(encrypted+index) += *(encrypted+index+16); + } + memset((encrypted+16), 0x00, 16); + + //3.d) send half sum + challenge_response(msg, encrypted); + request = message2buffer(msg); + hydra_send(sock, request, 42, 0); + free(msg); + free(request); + + //4) receive auth success/failure + index = 0; + while(index < 10) { //We're always expecting back a 42 byte buffer from a challenge request. + switch(hydra_data_ready(sock)) { + case -1: + hydra_report(stderr, "Error: Child with pid %d terminating, receive error\nerror:\t%s\n", (int32_t)getpid(), strerror(errno)); + hydra_child_exit(1); + break; + case 0: + //keep waiting... + break; + default: + bytecount = hydra_recv(sock, buffer+index, 10 - index); + if(bytecount < 0) { + hydra_report(stderr, "Error: Child with pid %d terminating, receive error\nerror:\t%s\n", (int32_t)getpid(), strerror(errno)); + hydra_child_exit(1); + } + index += bytecount; + } + } + msg = buffer2message(buffer); + switch(msg->type) { + case 0x0a: + hydra_completed_pair_found(); + break; + case 0x0b: + hydra_completed_pair(); + hydra_disconnect(sock); + break; + default: + hydra_report(stderr, "Error: Child with pid %d terminating, protocol error\n", (int32_t)getpid()); + hydra_child_exit(2); + } + } +#endif +} + +int32_t service_radmin2_init(char *ip, int32_t sp, unsigned char options, char *miscptr, FILE * fp, int32_t port, char *hostname) { + // called before the childrens are forked off, so this is the function + // which should be filled if initial connections and service setup has to be + // performed once only. + // + // fill if needed. + // + // return codes: + // 0 all OK + // -1 error, hydra will exit, so print a good error message here + + return 0; +} diff --git a/hydra.c b/hydra.c index 4171d29..d432c38 100644 --- a/hydra.c +++ b/hydra.c @@ -142,6 +142,11 @@ extern int32_t service_svn_init(char *ip, int32_t sp, unsigned char options, cha extern void service_oracle(char *ip, int32_t sp, unsigned char options, char *miscptr, FILE * fp, int32_t port, char *hostname); extern int32_t service_oracle_init(char *ip, int32_t sp, unsigned char options, char *miscptr, FILE * fp, int32_t port, char *hostname); #endif +#ifdef HAVE_GCRYPT +extern void service_radmin2(char *ip, int32_t sp, unsigned char options, char *miscptr, FILE * fp, int32_t port, char *hostname); +extern int32_t service_radmin2_init(char *ip, int32_t sp, unsigned char options, char *miscptr, FILE * fp, int32_t port, char *hostname); +#endif + extern int32_t service_adam6500_init(char *ip, int32_t sp, unsigned char options, char *miscptr, FILE * fp, int32_t port, char *hostname); extern int32_t service_cisco_init(char *ip, int32_t sp, unsigned char options, char *miscptr, FILE * fp, int32_t port, char *hostname); @@ -184,7 +189,7 @@ extern int32_t service_rpcap_init(char *ip, int32_t sp, unsigned char options, c // ADD NEW SERVICES HERE char *SERVICES = - "adam6500 asterisk afp cisco cisco-enable cvs firebird ftp ftps http[s]-{head|get|post} http[s]-{get|post}-form http-proxy http-proxy-urlenum icq imap[s] irc ldap2[s] ldap3[-{cram|digest}md5][s] mssql mysql ncp nntp oracle oracle-listener oracle-sid pcanywhere pcnfs pop3[s] postgres rdp redis rexec rlogin rpcap rsh rtsp s7-300 sapr3 sip smb smtp[s] smtp-enum snmp socks5 ssh sshkey svn teamspeak telnet[s] vmauthd vnc xmpp"; + "adam6500 asterisk afp cisco cisco-enable cvs firebird ftp ftps http[s]-{head|get|post} http[s]-{get|post}-form http-proxy http-proxy-urlenum icq imap[s] irc ldap2[s] ldap3[-{cram|digest}md5][s] mssql mysql ncp nntp oracle oracle-listener oracle-sid pcanywhere pcnfs pop3[s] postgres radmin2 rdp redis rexec rlogin rpcap rsh rtsp s7-300 sapr3 sip smb smtp[s] smtp-enum snmp socks5 ssh sshkey svn teamspeak telnet[s] vmauthd vnc xmpp"; #define MAXBUF 520 #define MAXLINESIZE ( ( MAXBUF / 2 ) - 4 ) @@ -482,6 +487,9 @@ static const struct { SERVICE3("telnet", telnet), SERVICE(vmauthd), SERVICE(vnc), +#ifdef HAVE_GCRYPT + SERVICE(radmin2), +#endif { "xmpp", service_xmpp_init, NULL, usage_xmpp } }; @@ -1331,6 +1339,7 @@ int32_t hydra_lookup_port(char *service) { {"s7-300", PORT_S7_300, PORT_S7_300_SSL}, {"rtsp", PORT_RTSP, PORT_RTSP_SSL}, {"rpcap", PORT_RPCAP, PORT_RPCAP_SSL}, + {"radmin2", PORT_RADMIN2, PORT_RADMIN2}, // ADD NEW SERVICES HERE - add new port numbers to hydra.h {"", PORT_NOPORT, PORT_NOPORT} }; @@ -3076,6 +3085,14 @@ int32_t main(int32_t argc, char *argv[]) { printf("[WARNING] the rdp module is currently reported to be unreliable, most likely against new Windows version. Please test, report - and if possible, fix.\n"); i = 1; } + if (strcmp(hydra_options.service, "radmin2") == 0) { +#ifdef HAVE_GCRYPT + i = 1; +#else + bail("hydra was not compiled with gcrypt support, radmin2 module can not be used"); +#endif + } + // ADD NEW SERVICES HERE diff --git a/hydra.h b/hydra.h old mode 100644 new mode 100755 index 62560d2..74a33fa --- a/hydra.h +++ b/hydra.h @@ -143,6 +143,7 @@ #define PORT_RTSP_SSL 554 #define PORT_RPCAP 2002 #define PORT_RPCAP_SSL 2002 +#define PORT_RADMIN2 4899 #define False 0 #define True 1