thc-hydra/hydra-snmp.c
2020-02-01 11:47:13 +01:00

598 lines
20 KiB
C

#include "hydra-mod.h"
#ifdef LIBOPENSSL
#include <openssl/aes.h>
#include <openssl/des.h>
#include <openssl/hmac.h>
#include <openssl/md5.h>
#include <openssl/sha.h>
#endif
extern int32_t hydra_data_ready_timed(int32_t socket, long sec, long usec);
extern char *HYDRA_EXIT;
extern int32_t child_head_no;
char snmpv3buf[1024], *snmpv3info = NULL;
int32_t snmpv3infolen = 0, snmpversion = 1, snmpread = 1, hashtype = 1, enctype = 0;
unsigned char snmpv3_init[] = {0x30, 0x3e, 0x02, 0x01, 0x03, 0x30, 0x11, 0x02, 0x04, 0x08, 0x86, 0xdd, 0xf0, 0x02, 0x03, 0x00, 0xff, 0xe3, 0x04, 0x01, 0x04, 0x02, 0x01, 0x03, 0x04, 0x10, 0x30, 0x0e, 0x04, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x30, 0x14, 0x04, 0x00, 0x04, 0x00, 0xa0, 0x0e, 0x02, 0x04, 0x3f, 0x44, 0x5c, 0xbc, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x30, 0x00};
unsigned char snmpv3_get1[] = {0x30, 0x77, 0x02, 0x01, 0x03, 0x30, 0x11, 0x02, 0x04, 0x08, 0x86, 0xdd, 0xef, 0x02, 0x03, 0x00, 0xff, 0xe3, 0x04, 0x01, 0x05, 0x02, 0x01, 0x03};
unsigned char snmpv3_get2[] = {0x30, 0x2e, 0x04, 0x0c, 0x80, 0x00, 0x00, 0x09, 0x03, 0x00, 0x00, 0x1f, 0xca, 0x8d, 0x82, 0x1b, 0x04, 0x00, 0xa0, 0x1c, 0x02, 0x04, 0x3f, 0x44, 0x5c, 0xbb, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x30, 0x0c, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x01, 0x00, 0x05, 0x00};
unsigned char snmpv3_nouser[] = {0x04, 0x00, 0x04, 0x00, 0x04, 0x00};
struct SNMPV1_A {
char ID;
char len;
char ver[3];
char comid;
char comlen;
};
struct SNMPV1_A snmpv1_a = {.ID = '\x30',
.len = '\x00',
.ver = "\x02\x01\x00", /* \x02\x01\x01 for snmpv2c, \x02\x01\x03 for snmpv3 */
.comid = '\x04',
.comlen = '\x00'};
struct SNMPV1_R {
unsigned char type[2];
unsigned char identid[2];
unsigned char ident[4];
unsigned char errstat[3];
unsigned char errind[3];
unsigned char objectid[2];
unsigned char object[11];
unsigned char value[3];
} snmpv1_r = {
.type = "\xa0\x1b", /* GET */
.identid = "\x02\x04",
.ident = "\x1a\x5e\x97\x00", /* random crap :) */
.errstat = "\x02\x01\x00", /* no error */
.errind = "\x02\x01\x00", /* error index 0 */
.objectid = "\x30\x0d",
.object = "\x30\x0b\x06\x07\x2b\x06\x01\x02\x01\x01\x01", /* sysDescr */
.value = "\x05\x00" /* we just read, so value = 0 */
};
struct SNMPV1_W {
unsigned char type[2];
unsigned char identid[2];
unsigned char ident[4];
unsigned char errstat[3];
unsigned char errind[3];
unsigned char objectid[2];
unsigned char object[12];
unsigned char value[8];
} snmpv1_w = {
.type = "\xa3\x21", /* SET */
.identid = "\x02\x04",
.ident = "\x1a\x5e\x97\x22", /* random crap :) */
.errstat = "\x02\x01\x00", /* no error */
.errind = "\x02\x01\x00", /* error index 0 */
.objectid = "\x30\x13", /* string */
.object = "\x30\x11\x06\x08\x2b\x06\x01\x02\x01\x01\x05\x00",
.value = "\x04\x05Hydra" /* writing hydra :-) */
};
#ifdef LIBOPENSSL
void password_to_key_md5(u_char *password, /* IN */
u_int passwordlen, /* IN */
u_char *engineID, /* IN - pointer to snmpEngineID */
u_int engineLength, /* IN - length of snmpEngineID */
u_char *key) { /* OUT - pointer to caller 16-octet buffer */
MD5_CTX MD;
u_char *cp, password_buf[80], *mypass = password, bpass[17];
u_long password_index = 0, count = 0, i, mylen, myelen = engineLength;
if (strlen(password) > passwordlen)
passwordlen = strlen(password);
if (passwordlen > sizeof(bpass) - 1)
passwordlen = sizeof(bpass) - 1;
mylen = passwordlen;
if (mylen < 8) {
memset(bpass, 0, sizeof(bpass));
strncpy(bpass, password, sizeof(bpass) - 1);
while (mylen < 8) {
strcat(bpass, password);
mylen += passwordlen;
}
mypass = bpass;
}
if (myelen > 32)
myelen = 32;
MD5_Init(&MD); /* initialize MD5 */
/* Use while loop until we've done 1 Megabyte */
while (count < 1048576) {
cp = password_buf;
for (i = 0; i < 64; i++) {
/* Take the next octet of the password, wrapping */
/* to the beginning of the password as necessary. */
*cp++ = mypass[password_index++ % mylen];
}
MD5_Update(&MD, password_buf, 64);
count += 64;
}
MD5_Final(key, &MD); /* tell MD5 we're done */
/* Now localize the key with the engineID and pass */
/* through MD5 to produce final key */
/* May want to ensure that engineLength <= 32, */
/* otherwise need to use a buffer larger than 64 */
memcpy(password_buf, key, 16);
memcpy(password_buf + 16, engineID, myelen);
memcpy(password_buf + 16 + myelen, key, 16);
MD5_Init(&MD);
MD5_Update(&MD, password_buf, 32 + myelen);
MD5_Final(key, &MD);
return;
}
void password_to_key_sha(u_char *password, /* IN */
u_int passwordlen, /* IN */
u_char *engineID, /* IN - pointer to snmpEngineID */
u_int engineLength, /* IN - length of snmpEngineID */
u_char *key) { /* OUT - pointer to caller 20-octet buffer */
SHA_CTX SH;
u_char *cp, password_buf[80], *mypass = password, bpass[17];
u_long password_index = 0, count = 0, i, mylen = passwordlen, myelen = engineLength;
if (mylen < 8) {
memset(bpass, 0, sizeof(bpass));
strcpy(bpass, password);
while (mylen < 8) {
strcat(bpass, password);
mylen += passwordlen;
}
mypass = bpass;
}
if (myelen > 32)
myelen = 32;
SHA1_Init(&SH); /* initialize SHA */
/* Use while loop until we've done 1 Megabyte */
while (count < 1048576) {
cp = password_buf;
for (i = 0; i < 64; i++) {
/* Take the next octet of the password, wrapping */
/* to the beginning of the password as necessary. */
*cp++ = mypass[password_index++ % mylen];
}
SHA1_Update(&SH, password_buf, 64);
count += 64;
}
SHA1_Final(key, &SH); /* tell SHA we're done */
/* Now localize the key with the engineID and pass */
/* through SHA to produce final key */
/* May want to ensure that engineLength <= 32, */
/* otherwise need to use a buffer larger than 72 */
memcpy(password_buf, key, 20);
memcpy(password_buf + 20, engineID, myelen);
memcpy(password_buf + 20 + myelen, key, 20);
SHA1_Init(&SH);
SHA1_Update(&SH, password_buf, 40 + myelen);
SHA1_Final(key, &SH);
return;
}
#endif
int32_t start_snmp(int32_t s, char *ip, int32_t port, unsigned char options, char *miscptr, FILE *fp) {
char *empty = "\"\"", *ptr, *login, *pass, buffer[1024], buf[1024], hash[64], key[256] = "", salt[8] = "";
int32_t i, j, k, size, off = 0, off2 = 0;
unsigned char initVect[8], privacy_params[8];
int32_t engine_boots = 0;
#ifdef LIBOPENSSL
DES_key_schedule symcbc;
#endif
if (strlen(login = hydra_get_next_login()) == 0)
login = empty;
if (strlen(pass = hydra_get_next_password()) == 0)
pass = empty;
if (snmpversion < 3) {
/* do we attack snmp v1 or v2c? */
if (snmpversion == 2) {
snmpv1_a.ver[2] = '\x01';
}
if (snmpread) {
size = sizeof(snmpv1_r);
} else {
size = sizeof(snmpv1_w);
}
snmpv1_a.comlen = (char)strlen(pass);
snmpv1_a.len = snmpv1_a.comlen + size + sizeof(snmpv1_a) - 3;
i = sizeof(snmpv1_a);
memcpy(buffer, &snmpv1_a, i);
strcpy(buffer + i, pass);
i += strlen(pass);
if (snmpread) {
memcpy(buffer + i, &snmpv1_r, size);
i += sizeof(snmpv1_r);
} else {
memcpy(buffer + i, &snmpv1_w, size);
i += sizeof(snmpv1_w);
}
} else { // snmpv3
if (enctype == 0) {
memcpy(buffer, snmpv3_get1, sizeof(snmpv3_get1));
i = sizeof(snmpv3_get1);
} else {
memcpy(buffer + 1, snmpv3_get1, sizeof(snmpv3_get1));
buffer[0] = buffer[1];
memset(buffer + 1, 0x81, 2);
i = sizeof(snmpv3_get1) + 1;
off2 = 1;
}
memcpy(buffer + i, snmpv3info, snmpv3infolen);
if (hashtype > 0) {
off = 12;
#ifdef LIBOPENSSL
if (hashtype == 1) {
password_to_key_md5(pass, strlen(pass), snmpv3info + 6, snmpv3info[5], key);
} else {
password_to_key_sha(pass, strlen(pass), snmpv3info + 6, snmpv3info[5], key);
}
#endif
if (enctype > 0) {
off += 8;
buffer[20 + off2] = 7;
}
} else {
ptr = login;
login = pass;
pass = ptr;
buffer[20] = 4;
}
buffer[i + 1] = 4 + snmpv3infolen + off + strlen(login);
buffer[i + 3] = 2 + snmpv3infolen + off + strlen(login);
if (enctype == 0)
buffer[1] = 48 + sizeof(snmpv3_get1) + buffer[i + 1];
i += snmpv3infolen;
// printf("2 + %d + %d + %d = 0x%02x\n", off, snmpv3infolen, strlen(login),
// buffer[1]);
buffer[i] = 0x04;
buffer[i + 1] = strlen(login);
memcpy(buffer + i + 2, login, strlen(login));
i += 2 + strlen(login);
buffer[i] = 0x04;
if (hashtype > 0) {
buffer[i + 1] = 12;
memset(buffer + i + 2, 0, 12);
off = i + 2;
i += 2 + 12;
} else {
buffer[i + 1] = 0;
i += 2;
}
buffer[i] = 0x04;
if (enctype == 0) {
buffer[i + 1] = 0x00;
i += 2;
} else {
buffer[i + 1] = 8;
memcpy(buffer + i + 2, salt, 8); // uninitialized and we don't care
i += 10;
}
if (enctype == 0) {
memcpy(buffer + i, snmpv3_get2, sizeof(snmpv3_get2));
i += sizeof(snmpv3_get2);
} else {
buffer[i] = 4;
buffer[i + 1] = 0x30;
#ifdef LIBOPENSSL
/*
//PrivDES::encrypt(const unsigned char *key,
// const uint32_t key_len,
// const unsigned char *buffer,
// const uint32_t buffer_len,
// unsigned char *out_buffer,
// uint32_t *out_buffer_len,
// unsigned char *privacy_params,
// uint32_t *privacy_params_len,
// const unsigned long engine_boots,
// const unsigned long engine_time)
// last 8 bytes of key are used as base for initialization vector */
k = 0;
memcpy((char *)initVect, key + 8, 8);
// put salt in privacy_params
j = htonl(engine_boots);
memcpy(privacy_params, (char *)&j, 4);
memcpy(privacy_params + 4, salt, 4); // ??? correct?
// xor initVect with salt
for (i = 0; i < 8; i++)
initVect[i] ^= privacy_params[i];
DES_key_sched((const_DES_cblock *)key, &symcbc);
DES_ncbc_encrypt(snmpv3_get2 + 2, buf, sizeof(snmpv3_get2) - 2, &symcbc, (const_DES_cblock *)(initVect), DES_ENCRYPT);
#endif
/* for (i = 0; i <= sizeof(snmpv3_get2) - 8; i += 8) {
DES_ncbc_encrypt(snmpv3_get2 + i, buf + i, 8,
(const_DES_cblock*)(initVect), DES_ENCRYPT);
}
// last part of buffer
if (buffer_len % 8) {
unsigned char tmp_buf[8];
unsigned char *tmp_buf_ptr = tmp_buf;
int32_t start = buffer_len - (buffer_len % 8);
memset(tmp_buf, 0, 8);
for (uint32_t l = start; l < buffer_len; l++)
*tmp_buf_ptr++ = buffer[l];
DES_ncbc_encrypt(tmp_buf, buf + start, 1, &symcbc,
(const_DES_cblock*)(initVect), DES_ENCRYPT); *out_buffer_len =
buffer_len + 8 - (buffer_len % 8); } else *out_buffer_len = buffer_len;
*/
// dummy
k = ((sizeof(snmpv3_get2) - 2) / 8);
if ((sizeof(snmpv3_get2) - 2) % 8 != 0)
k++;
memcpy(buffer + i + 2, buf, k * 8);
i += k * 8 + 2;
}
i++; // just to conform with the snmpv1/2 code
#ifdef LIBOPENSSL
if (hashtype == 1) {
HMAC((EVP_MD *)EVP_md5(), key, 16, buffer, i - 1, hash, NULL);
memcpy(buffer + off, hash, 12);
} else if (hashtype == 2) {
HMAC((EVP_MD *)EVP_sha1(), key, 20, buffer, i - 1, hash, NULL);
memcpy(buffer + off, hash, 12);
}
#endif
}
j = 0;
do {
if (hydra_send(s, buffer, i - 1, 0) < 0)
return 3;
j++;
} while (hydra_data_ready_timed(s, 1, 0) <= 0 && j < 3);
if (hydra_data_ready_timed(s, 5, 0) > 0) {
i = hydra_recv(s, (char *)buf, sizeof(buf));
if (snmpversion < 3) {
/* stolen from ADMsnmp... :P */
for (j = 0; j < i; j++) {
if (buf[j] == '\x04') { /* community name */
for (j = j + buf[j + 1]; j + 2 < i; j++) {
if (buf[j] == '\xa2') { /* PDU Response */
for (; j + 2 < i; j++) {
if (buf[j] == '\x02') { /* ID */
for (j = j + (buf[j + 1]); j + 2 < i; j++) {
if (buf[j] == '\x02') {
if (buf[j + 1] == '\x01') { /* good ! */
hydra_report_found_host(port, ip, "snmp", fp);
hydra_completed_pair_found();
if (memcmp(hydra_get_next_pair(), &HYDRA_EXIT, sizeof(HYDRA_EXIT)) == 0)
return 3;
return 1;
}
}
}
}
}
}
}
}
}
} else { // snmpv3 reply
off = 0;
if (buf[0] == 0x30) {
if (buf[4] == 0x03 && buf[5] == 0x30)
off = 4;
if (buf[5] == 0x03 && buf[6] == 0x30)
off = 6;
if (buf[6] == 0x03 && buf[7] == 0x30)
off = 6;
}
if (off == 0)
return 3;
if (debug)
printf("[DEBUG] buf[%d + 15] %d\n", off, buf[off + 15]);
k = 3 + off + buf[2 + off];
if ((j = hydra_memsearch(buf + k, buf[k + 3], snmpv3_nouser, sizeof(snmpv3_nouser))) < 0)
if ((j = hydra_memsearch(buf + k, buf[k + 3], login, strlen(login))) >= 0) {
if (snmpv3info[j - 2] == 0x04)
j -= 2;
else
j = -1;
}
if (j >= 0) {
i = buf[k + 3] + 4;
if (i > sizeof(snmpv3info))
i = sizeof(snmpv3info);
memcpy(snmpv3info, buf + k, i);
snmpv3infolen = j;
if (debug)
hydra_dump_asciihex(snmpv3info, snmpv3infolen);
}
if ((buf[off + 15] & 1) == 1) {
if (hashtype == 0)
hydra_report_found_host(port, ip, "snmp3", fp);
else
hydra_report_found_host(port, ip, "snmp", fp);
hydra_completed_pair_found();
if (memcmp(hydra_get_next_pair(), &HYDRA_EXIT, sizeof(HYDRA_EXIT)) == 0)
return 3;
return 1;
} else if ((buf[off + 15] & 5) == 4 && hydra_memsearch(buf, i, snmpv3_nouser,
sizeof(snmpv3_nouser)) >= 0) { // user does not exist
if (verbose)
printf("[INFO] user %s does not exist, skipping\n", login);
hydra_completed_pair_skip();
if (memcmp(hydra_get_next_pair(), &HYDRA_EXIT, sizeof(HYDRA_EXIT)) == 0)
return 3;
return 1;
}
}
}
hydra_completed_pair();
if (memcmp(hydra_get_next_pair(), &HYDRA_EXIT, sizeof(HYDRA_EXIT)) == 0)
return 3;
return 1;
}
void service_snmp(char *ip, int32_t sp, unsigned char options, char *miscptr, FILE *fp, int32_t port, char *hostname) {
int32_t run = 1, next_run = 1, sock = -1, i = 0;
int32_t myport = PORT_SNMP;
char *lptr;
if (miscptr != NULL) {
lptr = strtok(miscptr, ":");
while (lptr != NULL) {
if (strcasecmp(lptr, "1") == 0)
snmpversion = 1;
else if (strcasecmp(lptr, "2") == 0)
snmpversion = 2;
else if (strcasecmp(lptr, "3") == 0)
snmpversion = 3;
else if (strcasecmp(lptr, "PLAIN") == 0)
hashtype = 0;
else if (strcasecmp(lptr, "MD5") == 0)
hashtype = 1;
else if (strncasecmp(lptr, "R", 1) == 0)
snmpread = 1;
else if (strncasecmp(lptr, "W", 1) == 0)
snmpread = 0;
else if (strncasecmp(lptr, "SHA", 3) == 0)
hashtype = 2;
else if (strcasecmp(lptr, "DES") == 0)
enctype = 1;
else if (strcasecmp(lptr, "AES") == 0)
enctype = 2;
else {
fprintf(stderr, "[ERROR] unknown optional parameter: %s\n", lptr);
hydra_child_exit(2);
}
lptr = strtok(NULL, ":");
}
}
if (hashtype == 0)
enctype = 0;
if (port != 0)
myport = port;
sock = hydra_connect_udp(ip, myport);
port = myport;
if (debug)
printf("[DEBUG] snmpv%d, isread %d, hashtype %d, enctype %d\n", snmpversion, snmpread, hashtype, enctype);
hydra_register_socket(sp);
if (sock < 0) {
hydra_report(stderr, "[ERROR] Child with pid %d terminating, no socket available\n", (int32_t)getpid());
hydra_child_exit(1);
}
if (snmpversion == 3) {
next_run = 0;
while (snmpv3info == NULL && next_run < 3) {
hydra_send(sock, snmpv3_init, sizeof(snmpv3_init), 0);
if (hydra_data_ready_timed(sock, 5, 0) > 0) {
if ((i = hydra_recv(sock, (char *)snmpv3buf, sizeof(snmpv3buf))) > 30) {
if (snmpv3buf[4] == 3 && snmpv3buf[5] == 0x30) {
snmpv3info = snmpv3buf + 7 + snmpv3buf[6];
snmpv3infolen = snmpv3info[3] + 4;
if (snmpv3info + snmpv3infolen <= snmpv3buf + sizeof(snmpv3buf)) {
while (snmpv3info[snmpv3infolen - 2] == 4 && snmpv3info[snmpv3infolen - 1] == 0 && snmpv3infolen > 1)
snmpv3infolen -= 2;
if (debug)
hydra_dump_asciihex(snmpv3info, snmpv3infolen);
if (snmpv3info[10] == 3 && child_head_no == 0)
printf("[INFO] Remote device MAC address is "
"%02x:%02x:%02x:%02x:%02x:%02x\n",
(unsigned char)snmpv3info[12], (unsigned char)snmpv3info[13], (unsigned char)snmpv3info[14], (unsigned char)snmpv3info[15], (unsigned char)snmpv3info[16], (unsigned char)snmpv3info[12]);
}
}
}
}
next_run++;
}
if (snmpv3info == NULL || i < snmpv3info + snmpv3infolen - snmpv3buf) {
hydra_report(stderr, "No valid reply from snmp server, exiting!\n");
hydra_child_exit(2);
}
}
if (memcmp(hydra_get_next_pair(), &HYDRA_EXIT, sizeof(HYDRA_EXIT)) == 0)
run = 3;
while (1) {
switch (run) {
case 1: /* connect and service init function */
next_run = start_snmp(sock, ip, port, options, miscptr, fp);
break;
case 3: /* clean exit */
if (sock >= 0)
sock = hydra_disconnect(sock);
hydra_child_exit(2);
return;
default:
hydra_report(stderr, "[ERROR] Caught unknown return code, exiting!\n");
hydra_child_exit(2);
}
run = next_run;
}
}
int32_t service_snmp_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;
}
void usage_snmp(const char *service) {
printf("Module snmp is optionally taking the following parameters:\n"
" READ perform read requests (default)\n"
" WRITE perform write requests\n"
" 1 use SNMP version 1 (default)\n"
" 2 use SNMP version 2\n"
" 3 use SNMP version 3\n"
" Note that SNMP version 3 usually uses both login and "
"passwords!\n"
" SNMP version 3 has the following optional sub parameters:\n"
" MD5 use MD5 authentication (default)\n"
" SHA use SHA authentication\n"
" DES use DES encryption\n"
" AES use AES encryption\n"
" if no -p/-P parameter is given, SNMPv3 noauth is performed, "
"which\n"
" only requires a password (or username) not both.\n"
"To combine the options, use colons (\":\"), e.g.:\n"
" hydra -L user.txt -P pass.txt -m 3:SHA:AES:READ target.com snmp\n"
" hydra -P pass.txt -m 2 target.com snmp\n");
}