diff --git a/.gitignore b/.gitignore index 0a9a618..4cf0c32 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ hydra-gtk/stamp-h pw-inspector pw-inspector.exe hydra.restore +*~ diff --git a/Makefile.am b/Makefile.am index f238f47..2f15a1d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -21,7 +21,8 @@ SRC = hydra-vnc.c hydra-pcnfs.c hydra-rexec.c hydra-nntp.c hydra-socks5.c \ hydra-oracle-sid.c hydra-http-proxy.c hydra-http-form.c hydra-irc.c \ hydra-s7-300.c hydra-redis.c hydra-adam6500.c hydra-rtsp.c \ hydra-rpcap.c hydra-radmin2.c \ - hydra-time.c crc32.c d3des.c bfg.c ntlm.c sasl.c hmacmd5.c hydra-mod.c + hydra-time.c crc32.c d3des.c bfg.c ntlm.c sasl.c hmacmd5.c hydra-mod.c \ + hydra-smb2.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 \ @@ -34,7 +35,8 @@ OBJ = hydra-vnc.o hydra-pcnfs.o hydra-rexec.o hydra-nntp.o hydra-socks5.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 hydra-rtsp.o \ hydra-rpcap.o hydra-radmin2.o \ - crc32.o d3des.o bfg.o ntlm.o sasl.o hmacmd5.o hydra-mod.o hydra-time.o + crc32.o d3des.o bfg.o ntlm.o sasl.o hmacmd5.o hydra-mod.o hydra-time.o \ + hydra-smb2.o BINS = hydra pw-inspector EXTRA_DIST = README README.arm README.palm CHANGES TODO INSTALL LICENSE \ diff --git a/configure b/configure index 5afd0da..9cec404 100755 --- a/configure +++ b/configure @@ -72,6 +72,8 @@ MANDIR="" XHYDRA_SUPPORT="" FREERDP2_PATH="" WINPR2_PATH="" +SMBC_PATH="" +SMBC_IPATH="" if [ '!' "X" = "X$*" ]; then while [ $# -gt 0 ] ; do @@ -1178,6 +1180,54 @@ fi BSON_IPATH="" fi +echo "Checking for smbclient (libsmbclient.so, libsmbclient.h) ..." + + for i in $LIBDIRS ; do + if [ "X" = "X$SMBC_PATH" ]; then + if [ -f "$i/libsmbclient.so" -o -f "$i/libsmbclient.dylib" -o -f "$i/libsmbclient.a" ]; then + SMBC_PATH="$i" + fi + fi + if [ "X" = "X$SMBC_PATH" ]; then + TMP_LIB=`/bin/ls $i/libsmbclient.so* 2> /dev/null | grep smbclient` + if [ -n "$TMP_LIB" ]; then + SMBC_PATH="$i" + fi + fi + if [ "X" = "X$SMBC_PATH" ]; then + TMP_LIB=`/bin/ls $i/libsmbclient.dll* 2> /dev/null | grep smbclient` + if [ -n "$TMP_LIB" ]; then + SMBC_PATH="$i" + fi + fi + done + + SMBC_IPATH= + for i in $INCDIRS ; do + if [ "X" = "X$SMBC_IPATH" ]; then + if [ -f "$i/libsmbclient.h" ]; then + SMBC_IPATH="$i" + fi + if [ -f "$i/samba-4.0/libsmbclient.h" ]; then + SMBC_IPATH="$i/samba-4.0" + fi + fi + done + + if [ "X" != "X$DEBUG" ]; then + echo DEBUG: SMBC_PATH=$SMBC_PATH/libsmbclient + echo DEBUG: SMBC_IPATH=$SMBC_IPATH/libsmbclient.h + fi + if [ -n "$SMBC_PATH" -a -n "$SMBC_IPATH" ]; then + echo " ... found" + fi + if [ "X" = "X$SMBC_PATH" -o "X" = "X$SMBC_IPATH" ]; then + echo " ... NOT found, module smb2 disabled" + SMBC_PATH="" + SMBC_IPATH="" + fi + + if [ "X" = "X$XHYDRA_SUPPORT" ]; then echo "Checking for GUI req's (pkg-config, gtk+-2.0) ..." XHYDRA_SUPPORT=`pkg-config --help > /dev/null 2>&1 || echo disabled` @@ -1271,7 +1321,29 @@ XLIBS="" XLIBPATHS="" XIPATHS="" -if [ -n "$FIREBIRD_PATH" -o -n "$PCRE_PATH" -o -n "$IDN_PATH" -o -n "$SSL_PATH" -o -n "$CRYPTO_PATH" -o -n "$NSL_PATH" -o -n "$SOCKET_PATH" -o -n "$RESOLV_PATH" -o -n "$SAPR3_PATH" -o -n "$SSH_PATH" -o -n "$POSTGRES_PATH" -o -n "$SVN_PATH" -o -n "$NCP_PATH" -o -n "$CURSES_PATH" -o -n "$ORACLE_PATH" -o -n "$AFP_PATH" -o -n "$MYSQL_PATH" -o -n "$MCACHED_PATH" -o -n "$MONGOD_PATH" -o -n "$FREERDP2_PATH" -o -n "$WINPR2_PATH" ]; then +if [ -n "$FIREBIRD_PATH" -o \ + -n "$PCRE_PATH" -o \ + -n "$IDN_PATH" -o \ + -n "$SSL_PATH" -o \ + -n "$CRYPTO_PATH" -o \ + -n "$NSL_PATH" -o \ + -n "$SOCKET_PATH" -o \ + -n "$RESOLV_PATH" -o \ + -n "$SAPR3_PATH" -o \ + -n "$SSH_PATH" -o \ + -n "$POSTGRES_PATH" -o \ + -n "$SVN_PATH" -o \ + -n "$NCP_PATH" -o \ + -n "$CURSES_PATH" -o \ + -n "$ORACLE_PATH" -o \ + -n "$AFP_PATH" -o \ + -n "$MYSQL_PATH" -o \ + -n "$MCACHED_PATH" -o \ + -n "$MONGOD_PATH" -o \ + -n "$FREERDP2_PATH" -o \ + -n "$WINPR2_PATH" -o \ + -n "$SMBC_PATH" \ + ]; then if [ "$SYSS" = "Darwin" ] && [ ! -d "/lib" ]; then #for libraries installed with MacPorts if [ -d "/opt/local/lib" ]; then @@ -1359,9 +1431,35 @@ fi if [ -n "$WINPR2_PATH" ]; then XDEFINES="$XDEFINES -DLIBWINPR2" fi +if [ -n "$SMBC_PATH" ]; then + XDEFINES="$XDEFINES -DLIBSMBCLIENT" +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 $MCACHED_PATH $MONGODB_PATH $BSON_PATH $FREERDP2_PATH $WINPR2_PATH; do +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 \ + $MCACHED_PATH \ + $MONGODB_PATH \ + $BSON_PATH \ + $FREERDP2_PATH \ + $WINPR2_PATH \ + $SMBC_PATH; do if [ "$OLDPATH" = "$i" ]; then OLDPATH="$i" else @@ -1423,6 +1521,9 @@ fi if [ -n "$FREERDP2_IPATH" ]; then XIPATHS="$XIPATHS -I$FREERDP2_IPATH -I$WINPR2_IPATH" fi +if [ -n "$SMBC_IPATH" ]; then + XIPATHS="$XIPATHS -I$SMBC_IPATH" +fi if [ -n "$HAVE_GCRYPT" ]; then XLIBS="$XLIBS -lgcrypt" fi @@ -1501,6 +1602,9 @@ fi if [ -n "$WINPR2_PATH" ]; then XLIBS="$XLIBS -lwinpr2" fi +if [ -n "$SMBC_PATH" ]; then + XLIBS="$XLIBS -lsmbclient" +fi if [ -d /usr/kerberos/include ]; then XIPATHS="$XIPATHS -I/usr/kerberos/include" fi diff --git a/hydra-smb2.c b/hydra-smb2.c new file mode 100644 index 0000000..f42f074 --- /dev/null +++ b/hydra-smb2.c @@ -0,0 +1,304 @@ +/** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * Copyright (C) 2020 Karim Kanso, all rights reserved. + * kaz 'dot' kanso 'at' g mail 'dot' com + */ + +#if defined(LIBSMBCLIENT) + +#include "hydra-mod.h" + +#include +#include +#include +#include +#include + +extern char *HYDRA_EXIT; + +typedef struct creds { + const char* workgroup; + const char* user; + const char* pass; +} creds_t; + + +const char default_workgroup[] = "WORKGROUP"; +bool use_nt_hash = false; +const char* workgroup = default_workgroup; +const char* netbios_name = NULL; + +#define EXIT_PROTOCOL_ERROR hydra_child_exit(2) +#define EXIT_CONNECTION_ERROR hydra_child_exit(1) +#define EXIT_NORMAL hydra_child_exit(0) + +void smb2_auth_provider(SMBCCTX *c, + const char *srv, + const char *shr, + char *wg, int wglen, + char *un, int unlen, + char *pw, int pwlen) { + creds_t* cr = (creds_t*)smbc_getOptionUserData(c); + strncpy(wg, cr->workgroup, wglen); + strncpy(un, cr->user, unlen); + strncpy(pw, cr->pass, pwlen); + wg[wglen-1] = 0; + un[unlen-1] = 0; + pw[pwlen-1] = 0; +} + +bool smb2_run_test(creds_t* cr, const char* server, uint16_t port) { + SMBCCTX* ctx = smbc_new_context(); + if (ctx == NULL) { + hydra_report(stderr, "[ERROR] failed to create context\n"); + EXIT_PROTOCOL_ERROR; + } + // samba internal debugging will be dumped to stderr + smbc_setDebug(ctx, debug ? 7 : 0); + smbc_setOptionDebugToStderr(ctx, true); + smbc_setFunctionAuthDataWithContext(ctx, smb2_auth_provider); + smbc_setOptionUserData(ctx, cr); + // 0 will use default port + smbc_setPort(ctx, port); + smbc_setOptionNoAutoAnonymousLogin(ctx, false); + smbc_setOptionUseNTHash(ctx, use_nt_hash); + if (netbios_name) { + smbc_setNetbiosName(ctx, (char*)netbios_name); + } + + ctx = smbc_init_context(ctx); + if (!ctx) { + hydra_report(stderr, "[ERROR] smbc_init_context fail\n"); + smbc_free_context(ctx, 1); + EXIT_PROTOCOL_ERROR; + } + + char uri[2048]; + snprintf(uri, sizeof(uri) - 1, "smb://%s/IPC$", server); + uri[sizeof(uri)-1] = 0; + if (verbose) { + printf("[INFO] Connecting to: %s with %s\\%s%%%s\n", + uri, cr->workgroup, + cr->user, + cr->pass); + } + SMBCFILE *fd = smbc_getFunctionOpendir(ctx)(ctx, uri); + if (fd) { + hydra_report(stderr, "[WARNING] Unexpected open on IPC$\n"); + smbc_getFunctionClosedir(ctx)(ctx, fd); + smbc_free_context(ctx, 1); + fd = NULL; + return true; + } + + /* + errno is set to 22 (EINVAL) when IPC$ as been opened but can not + be opened like a normal share. This corresponds to samba error + NT_STATUS_INVALID_INFO_CLASS, however this precise error code is + not available outside of the library. Thus, instead the library + sets a generic error (EINVAL) which can also correspond to other + cases (see below test). + + This is not ideal, but appears to be the best that the + libsmbclient library offers as detailed state information is + internalised and not available. Further, it is also not possible + from the api to separate the connection, authentication and + authorisation. + + The following text is taken from the libsmbclient header file for + the return value of the smbc_getFunctionOpendir function: + + Valid directory handle. < 0 on error with errno set: + - EACCES Permission denied. + - EINVAL A NULL file/URL was passed, or the URL would + not parse, or was of incorrect form or smbc_init not + called. + - ENOENT durl does not exist, or name is an + - ENOMEM Insufficient memory to complete the + operation. + - ENOTDIR name is not a directory. + - EPERM the workgroup could not be found. + - ENODEV the workgroup or server could not be found. + + */ + switch (errno) { + case EINVAL: // 22 + // probably password ok + smbc_free_context(ctx, 1); + return true; + break; + case EACCES: + // 100% access denied + break; + case EHOSTUNREACH: + case ETIMEDOUT: + case ECONNREFUSED: + // there are probably more codes that could be added here to + // indicate connection errors. + smbc_free_context(ctx, 1); + EXIT_CONNECTION_ERROR; + break; + default: + // unexpected error + hydra_report(stderr, "[ERROR] %s (%d)\n", strerror(errno), errno); + smbc_free_context(ctx, 1); + EXIT_PROTOCOL_ERROR; + } + + smbc_free_context(ctx, 1); + return false; +} + +void service_smb2(char *ip, + int32_t sp, + unsigned char options, + char *miscptr, + FILE * fp, + int32_t port, + char *hostname) { + hydra_register_socket(sp); + while (memcmp(hydra_get_next_pair(), &HYDRA_EXIT, sizeof(HYDRA_EXIT))) { + char *login, *pass; + + login = hydra_get_next_login(); + pass = hydra_get_next_password(); + + creds_t cr = { + .user = login, + .pass = pass, + .workgroup = workgroup, + }; + + if (smb2_run_test(&cr, hydra_address2string(ip), port & 0xffff)) { + hydra_completed_pair_found(); + } else { + hydra_completed_pair(); + } + } + EXIT_NORMAL; +} + +// constants used by option parser +const char tkn_workgroup[] = "workgroup:{"; +const char tkn_nthash_true[] = "nthash:true"; +const char tkn_nthash_false[] = "nthash:false"; +const char tkn_netbios[] = "netbios:{"; + +#define CMP(s1, s2) (strncmp(s1, s2, sizeof(s1) - 1) == 0) + +int32_t service_smb2_init(char *ip, + int32_t sp, + unsigned char options, + char *miscptr, + FILE * fp, + int32_t port, + char *hostname) { + if (!miscptr) + return 0; + + while(*miscptr) { + if (isspace(*miscptr)) { + miscptr++; + continue; + } + if (CMP(tkn_workgroup, miscptr)) { + miscptr += sizeof(tkn_workgroup) - 1; + char* p = strchr(miscptr, '}'); + if (p == NULL) { + hydra_report(stderr, "[ERROR] missing closing brace in workgroup\n"); + return -1; + } + *p = '\0'; + workgroup = miscptr; + miscptr = p + 1; + if (verbose || debug) { + printf("[VERBOSE] Set workgroup to: %s\n", workgroup); + } + continue; + } + if (CMP(tkn_netbios, miscptr)) { + miscptr += sizeof(tkn_netbios) - 1; + char* p = strchr(miscptr, '}'); + if (p == NULL) { + hydra_report(stderr, "[ERROR] missing closing brace in netbios name\n"); + return -1; + } + *p = '\0'; + netbios_name = miscptr; + miscptr = p + 1; + if (verbose || debug) { + printf("[VERBOSE] Set netbios name to: %s\n", netbios_name); + } + continue; + } + if (CMP(tkn_nthash_true, miscptr)) { + miscptr += sizeof(tkn_nthash_true) - 1; + use_nt_hash = true; + if (verbose || debug) { + printf("[VERBOSE] Enabled nthash.\n"); + } + continue; + } + if (CMP(tkn_nthash_false, miscptr)) { + miscptr += sizeof(tkn_nthash_false) - 1; + use_nt_hash = false; + if (verbose || debug) { + printf("[VERBOSE] Disabled nthash.\n"); + } + continue; + } + + hydra_report(stderr, "[ERROR] unable to parse: %s\n", miscptr); + return -1; + } + + return 0; +} + +void usage_smb2(const char* service) { + puts("Module is a thin wrapper over the Samba client library (libsmbclient).\n" + "Thus, is capable of negotiating v1, v2 and v3 of the protocol.\n" + "\n" + "As this relies on Samba libraries, the system smb.conf will be parsed\n" + "when library starts up. It is possible to add configuration options\n" + "into that file that affect this module (such as min/max supported\n" + "protocol version).\n" + "\n" + "Caution: due to the high-level libsmbclient api (compared the smb\n" + "Hydra module), the accuracy is reduced. That is, this module works by\n" + "attempting to open the IPC$ share, which is reported as an error,\n" + "e.g. try this with the smbclient tool and it will raise the\n" + "NT_STATUS_INVALID_INFO_CLASS error). Sadly, the level of feedback\n" + "from the api does not distinguish this error from general/unknown\n" + "errors, so it might be possible to have false positives due to this\n" + "fact. One example of this is when the library can not parse the uri\n" + "correctly. On the other hand, false negatives could occur when a\n" + "valid credential is unable to open the share due to access control,\n" + "e.g. a locked/suspended account.\n" + "\n" + "There are three module options available:\n" + " workgroup:{XXX} - set the users workgroup\n" + " netbios:{XXX} - set the recipients netbios name\n" + " nthash:true or nthash:false - threat password as an nthash\n" + "\n" + "Examples: \n" + " hydra smb2://abc.com -l admin -p xxx -m workgroup:{OFFICE}\n" + " hydra smb2://1.2.3.4 -l admin -p F54F3A1D3C38140684FF4DAD029F25B5 -m 'workgroup:{OFFICE} nthash:true'\n" + " hydra -l admin -p F54F3A1D3C38140684FF4DAD029F25B5 'smb2://1.2.3.4/workgroup:{OFFICE} nthash:true'\n" + ); +} + +#endif // LIBSMBCLIENT diff --git a/hydra.c b/hydra.c index 37e1323..28365f8 100644 --- a/hydra.c +++ b/hydra.c @@ -45,6 +45,7 @@ void usage_http_proxy(const char* service); void usage_http_proxy_urlenum(const char* service); void usage_snmp(const char* service); void usage_http(const char* service); +void usage_smb2(const char* service); extern void service_asterisk(char *ip, int32_t sp, unsigned char options, char *miscptr, FILE * fp, int32_t port, char *hostname); @@ -92,6 +93,10 @@ extern void service_rpcap(char *ip, int32_t sp, unsigned char options, char *mis // ADD NEW SERVICES HERE +#if defined(LIBSMBCLIENT) +extern int32_t service_smb2_init(char *ip, int32_t sp, unsigned char options, char *miscptr, FILE * fp, int32_t port, char *hostname); +extern void service_smb2(char *ip, int32_t sp, unsigned char options, char *miscptr, FILE * fp, int32_t port, char *hostname); +#endif #ifdef HAVE_MATH_H extern void service_mysql(char *ip, int32_t sp, unsigned char options, char *miscptr, FILE * fp, int32_t port, char *hostname); @@ -196,7 +201,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[s] 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] memcached mongodb 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"; + "adam6500 asterisk afp cisco cisco-enable cvs firebird ftp[s] 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] memcached mongodb 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 smb2 smtp[s] smtp-enum snmp socks5 ssh sshkey svn teamspeak telnet[s] vmauthd vnc xmpp"; #define MAXBUF 520 #define MAXLINESIZE ( ( MAXBUF / 2 ) - 4 ) @@ -437,6 +442,9 @@ SERVICE3("mongodb", mongodb), SERVICE(sip), SERVICE3("smbnt", smb), SERVICE3("smb", smb), +#endif +#if defined(LIBSMBCLIENT) + SERVICE3("smb2", smb2), #endif SERVICE3("smtp", smtp), SERVICE3("smtp-enum", smtp_enum), @@ -1288,6 +1296,7 @@ int32_t hydra_lookup_port(char *service) { {"rsh", PORT_RSH, PORT_RSH_SSL}, {"sapr3", PORT_SAPR3, PORT_SAPR3_SSL}, {"smb", PORT_SMBNT, PORT_SMBNT_SSL}, + {"smb2", PORT_SMBNT, PORT_SMBNT_SSL}, {"smbnt", PORT_SMBNT, PORT_SMBNT_SSL}, {"socks5", PORT_SOCKS5, PORT_SOCKS5_SSL}, {"ssh", PORT_SSH, PORT_SSH_SSL}, @@ -2152,6 +2161,10 @@ int main(int argc, char *argv[]) { SERVICES = hydra_string_replace(SERVICES, "svn ", ""); strcat(unsupported, "svn "); #endif +#if !defined(LIBSMBCLIENT) + SERVICES = hydra_string_replace(SERVICES, "smb2 ", ""); + strcat(unsupported, "smb2 "); +#endif #ifndef LIBOPENSSL // for ftps @@ -2801,6 +2814,25 @@ int main(int argc, char *argv[]) { bail("Compiled without OPENSSL support, module not available!"); #endif } + if (strcmp(hydra_options.service, "smb2") == 0) { +#if !defined(LIBSMBCLIENT) + bail("Compiled without LIBSMBCLIENT support, module not available!"); +#else + if (hydra_options.login != NULL && + (index(hydra_options.login, '\\') != NULL || + index(hydra_options.login, '/') != NULL)) + fprintf(stderr, + "[WARNING] potential windows domain specification found in " + "login. You must use the -m option to pass a domain.\n"); + if (hydra_options.miscptr == NULL || \ + (strlen(hydra_options.miscptr) == 0)) { + fprintf(stderr, + "[WARNING] Workgroup was not specified, using \"WORKGROUP\"\n"); + } + i = 1; +#endif + } + if (strcmp(hydra_options.service, "rdp") == 0){ #ifndef LIBFREERDP2 bail("Compiled without FREERDP2 support, module not available!");