/* * smb-share-enum * * By Jon Hart * * Written to emulate what the Windows tool 'Legion' does and perhaps * a little more. * * This grew from the need to, in a pinch, find all Windows/Samba shares on * a network that were writable. * * Requires libsmbclient and/or libsmbclient-dev, available as source * and/or packages for most major Linux/BSD distributions. * * Build instructions: * * `gcc -Wall -lsmbclient -o smb-share-enum smb-share-enum.c` * * Sample run in normal mode: * * smb://TUVALU/D$ is writable * smb://TUVALU/C$ is writable * smb://TUVALU/WAREZ is writable * smb://CONGO/tmp is writable * * Sample run in 'browse only' mode: * * Workgroup SPOOFED.ORG * Server CONGO * Share smb://CONGO/IPC$ * Share smb://CONGO/CD * Share smb://CONGO/mp3 * Share smb://CONGO/tmp * Workgroup MSHOME * Server WINXP * Share smb://WINXP/Jon's Camera * Share smb://WINXP/ADMIN$ * Share smb://WINXP/F$ * Share smb://WINXP/2006 * Share smb://WINXP/HPDeskJet * Share smb://WINXP/print$ * Share smb://WINXP/SharedDocs * Share smb://WINXP/IPC$ * Share smb://WINXP/E$ * * * This tool is not particularly fast, but I'm not sure that it an be made * any faster. I also need to rework the output in verbose mode, as it gets * ugly. */ #include #include #include #include #include #include #include #include int browse_depth = 0; typedef struct smbitem smbitem; struct smbitem { smbitem *next; int type; char name[1]; }; #define OPT_KRB 0 #define OPT_USER 1 #define OPT_PASSWD 2 #define OPT_WORKGROUP 3 #define OPT_DEBUG 4 #define OPT_VERBOSE 5 #define OPT_BROWSE 6 struct opt { char *name; /* name */ int set; char *value; }; struct opt opts[] = { {"kerberos", 0, NULL} , {"user", 0, NULL}, {"password", 0, NULL}, {"workgroup", 0, NULL}, {"debug", 0, NULL}, {"verbose", 0, NULL}, {"browse", 0, NULL} }; void delete_smbctx(SMBCCTX* ctx); void recurse(SMBCCTX *ctx, char *smb_group, char *smb_path, int maxlen); void test_write(SMBCCTX *ctx, char *smb_path); void smbc_auth_fn ( const char *server, const char *share, char *wrkgrp, int wrkgrplen, char *user, int userlen, char *passwd, int passwdlen) { } SMBCCTX* create_smbctx() { SMBCCTX *ctx; if ((ctx = smbc_new_context()) == NULL) { fprintf(stderr, "Failed to create new context: %s\n", strerror(errno)); delete_smbctx(ctx); return NULL; } if (opts[OPT_DEBUG].set) { ctx->debug = atoi(opts[OPT_DEBUG].value); } else { ctx->debug = 0; } /* if this isn't here, even though it points to a useless function, * nothing works. XXXX FIGURE THIS OUT. */ ctx->callbacks.auth_fn = smbc_auth_fn; if (opts[OPT_WORKGROUP].set) { if ((ctx->workgroup = malloc(strlen(opts[OPT_WORKGROUP].value))) == NULL) { fprintf(stderr, "Couldn't malloc() for ctx->workgroup: %s\n", strerror(errno)); delete_smbctx(ctx); return NULL; } else { strncpy(ctx->workgroup, opts[OPT_WORKGROUP].value, strlen(opts[OPT_WORKGROUP].value)); } } if (opts[OPT_USER].set) { if ((ctx->user = malloc(strlen(opts[OPT_USER].value))) == NULL) { fprintf(stderr, "Couldn't malloc() ctx->user: %s\n", strerror(errno)); delete_smbctx(ctx); return NULL; } else { strncpy(ctx->user, opts[OPT_USER].value, strlen(opts[OPT_USER].value)); } } if (opts[OPT_KRB].set) { ctx->flags = SMB_CTX_FLAG_USE_KERBEROS; } if ((ctx = smbc_init_context(ctx)) == NULL) { fprintf(stderr, "Failed to init context: %s\n", strerror(errno)); return NULL; } return ctx; } void delete_smbctx(SMBCCTX* ctx) { if (ctx->callbacks.purge_cached_fn(ctx) == 1) { fprintf(stderr, "Couldn't remove servers!\n"); } if (smbc_free_context(ctx, 0) == 1) { fprintf(stderr, "Couldn't free context: %s\n", strerror(errno)); } } smbitem* get_smbitem_list(SMBCCTX *ctx, char *smb_path) { SMBCFILE *fd; struct smbc_dirent *dirent; smbitem *list = NULL, *item; if ((fd = ctx->opendir(ctx, smb_path)) == NULL) { fprintf(stderr, "Couldn't browse %s: %s\n", smb_path, strerror(errno)); return NULL; } while ((dirent = ctx->readdir(ctx, fd)) != NULL) { if (strcmp(dirent->name, "") == 0) continue; if (strcmp(dirent->name, ".") == 0) continue; if (strcmp(dirent->name, "..") == 0) continue; if ((item = malloc(sizeof(smbitem) + strlen(dirent->name))) == NULL) { fprintf(stderr, "Couldn't malloc() for item: %s\n", strerror(errno)); continue; } item->next = list; item->type = dirent->smbc_type; strcpy(item->name, dirent->name); list = item; } ctx->closedir(ctx, fd); return (list); } void help(char *name) { fprintf(stderr, "%s -- A SMB share enumerator, write tester\n", name); fprintf(stderr, "by Jon Hart \n"); fprintf(stderr, "\nUsage:\n"); fprintf(stderr, "\t%s [options] [IP | hostname | NETBIOS name | Workgroup/Domain]\n", name); fprintf(stderr, "\tOptions:\n"); fprintf(stderr, "\t-b # 'browse only'\n"); fprintf(stderr, "\t-d # set debug level of underlying SMB stuff\n"); fprintf(stderr, "\t-k # user kerberos (don't forget to kinit first...)\n"); fprintf(stderr, "\t-u \n"); fprintf(stderr, "\t-v # be verbose\n"); fprintf(stderr, "\t-w # workgroup/domain\n"); fprintf(stderr, "\n\tHint: don't specify a hostname, IP, name, etc to force autodiscovery!\n"); } void recurse(SMBCCTX *ctx, char *smb_group, char *smb_path, int maxlen) { int len, i; smbitem *list, *item; len = strlen(smb_path); list = get_smbitem_list(ctx, smb_path); while (list != NULL) { switch (list->type) { case SMBC_WORKGROUP: browse_depth=1; if (opts[OPT_VERBOSE].set || opts[OPT_BROWSE].set) { printf("Workgroup %s\n", list->name); } smb_group = list->name; if ((size_t) maxlen < 7 + strlen(list->name)) break; strcpy(smb_path + 6, list->name); recurse(ctx, smb_group, smb_path, maxlen); browse_depth--; ctx->callbacks.purge_cached_fn(ctx); break; case SMBC_SERVER: browse_depth++; if (opts[OPT_VERBOSE].set || opts[OPT_BROWSE].set) { for (i = 1; i < browse_depth; i++) { printf("\t"); } printf("Server %s\n", list->name); } if ((size_t) maxlen < 7 + strlen(list->name)) break; strcpy(smb_path + 6, list->name); recurse(ctx, smb_group, smb_path, maxlen); browse_depth--; ctx->callbacks.purge_cached_fn(ctx); break; case SMBC_FILE_SHARE: case SMBC_PRINTER_SHARE: case SMBC_COMMS_SHARE: case SMBC_IPC_SHARE: browse_depth++; if ((size_t) maxlen < len + strlen(list->name) + 2) break; smb_path[len] = '/'; strcpy(smb_path + len + 1, list->name); if (opts[OPT_VERBOSE].set || opts[OPT_BROWSE].set) { for (i = 1; i < browse_depth; i++) { printf("\t"); } printf("Share %s\n", smb_path); } if (!opts[OPT_BROWSE].set && list->type == SMBC_FILE_SHARE) { test_write(ctx, smb_path); } browse_depth--; break; } item = list; list = list->next; free(item); } free(list); smb_path[len] = '\0'; } void test_write(SMBCCTX *ctx, char *share) { SMBCFILE *fd; int i, file_len, full_path_len; char *full_path; char *file; struct timeval tv; gettimeofday(&tv, NULL); srandom((u_int) tv.tv_usec); file_len = strlen("/XXXXXXXXXX-write.txt"); full_path_len = file_len + strlen(share); if ((file = malloc((size_t) (file_len + 1))) == NULL) { fprintf(stderr, "Couldn't malloc() for file: %s\n", strerror(errno)); return; } if ((memset(file, '\0', (size_t) file_len + 1)) == NULL) { free(file); fprintf(stderr, "Couldn't memset() file: %s\n", strerror(errno)); return; } /* file will be of the format '/XXXXXXXXXX-write.txt' */ sprintf(file, "/%010ld-write.txt", random()); if ((full_path = malloc((size_t) full_path_len + 1)) == NULL) { free(file); fprintf(stderr, "Couldn't malloc() for full_path: %s\n", strerror(errno)); return; } if ((memset(full_path, '\0', (size_t) full_path_len + 1)) == NULL) { free(file); free(full_path); fprintf(stderr, "Couldn't memset() full_path: %s\n", strerror(errno)); return; } strncpy(full_path, share, strlen(share)); strncat(full_path, file, strlen(file)); if ((fd = ctx->creat(ctx, full_path, 700)) == NULL) { if (opts[OPT_VERBOSE].set) { fprintf(stderr, "Couldn't write %s: %s\n", full_path, strerror(errno)); } free(file); free(full_path); return; } if (opts[OPT_VERBOSE].set) { for (i = 1; i <= browse_depth; i++) { printf("\t"); } printf("Wrote %s\n", full_path); } fd = (SMBCFILE *) ctx->close_fn(ctx, fd); if ((fd = (SMBCFILE *) ctx->unlink(ctx, full_path)) == 0) { if (opts[OPT_VERBOSE].set) { for (i = 1; i <= browse_depth; i++) { printf("\t"); } printf("Removed %s\n", full_path); } fd = (SMBCFILE *) ctx->close_fn(ctx, fd); } else { fprintf(stderr, "Couldn't remove %s: %s\n", full_path, strerror(errno)); } printf("%s is writable\n", share); free(full_path); free(file); return; } int main(int argc, char *argv[]) { int i, c; SMBCCTX *ctx; char smb_path[32768] = "smb://"; while ((c = getopt(argc, argv, "bd:hku:vw:")) != EOF) { switch (c) { case 'b': opts[OPT_BROWSE].set = 1; break; case 'd': opts[OPT_DEBUG].set = 1; opts[OPT_DEBUG].value = optarg; break; case 'h': help(argv[0]); return(0); case 'k': opts[OPT_KRB].set = 1; break; case 'u': opts[OPT_USER].set = 1; opts[OPT_USER].value = optarg; break; case 'v': opts[OPT_VERBOSE].set = 1; break; case 'w': opts[OPT_WORKGROUP].set = 1; opts[OPT_WORKGROUP].value = optarg; strncat(smb_path, optarg, strlen(optarg)); break; default: break; } } if ((ctx = create_smbctx()) == NULL) { fprintf(stderr, "Couldn't create context: %s\n", strerror(errno)); if (smbc_free_context(ctx, 0) == 1) { fprintf(stderr, "Couldn't free context: %s\n", strerror(errno)); } return(1); } if (optind == argc) { /* no args specified. will find/check everything that responds */ if (opts[OPT_VERBOSE].set) { printf("Scanning all visible workgroups, domains, hosts, etc...\n"); } recurse(ctx, "", smb_path, sizeof(smb_path)); } else { /* check each of the args specified, which can be an IP address, hostname * netbios name, workgroup, etc */ for (i = optind; i < argc; i++) { if (opts[OPT_VERBOSE].set) { printf("Scanning %s\n", argv[i]); } strncpy(smb_path + 6, argv[i], sizeof(smb_path) - 7); smb_path[sizeof(smb_path) - 1] = '\0'; recurse(ctx, "", smb_path, sizeof(smb_path)); } } delete_smbctx(ctx); return(0); }