home *** CD-ROM | disk | FTP | other *** search
- ---[ Phrack Magazine Volume 8, Issue 52 January 26, 1998, article 10 of 20
-
-
- -------------------------[ a Quick nT Interrogation Probe (QTIP)
-
-
- --------[ twitch <twitch@aye.net>
-
-
- ----[ INTRODUCTION
-
-
- As you probably already know, certain LanMan derivatives (most notably
- Windows NT) sport a stupid feature known as `null sessions`. Null sessions
- allow server connections to be established without the hassle and rigmarole of
- username or password authentication. This is reportedly to ease
- administrative tasks (UserManager and ilk utilize them). Also, such silliness
- such as the RedButton bug have shown (although in poor form) that an
- interested/malicious third party can gleen quite a bit of info from `Press any
- key to return to index`. Once established, these connections default to having
- permissions to display enumerated user and share lists, get information about
- particular users, wander the registry, etc. QTIP takes advantage of this,
- allowing the user to procure far too much information about the target
- machine. It employs no black magic or hidden technique to do this. QTIP
- works via straight API calls.
-
- As of service pack 3 for NT 4.0, it is possible for the `informed` system
- administrator to block null sessions through the registry, effectively
- nullifying any threat from QTIP. I do not, however, believe that there is
- such a patch for 3.5.1 machines. Also, it has not been tested against SAMBA
- servers, and as far as the author knows, SAMBA does not support something as
- asinine as null sessions (anyone who knows any differently is invited to mail
- corrections to the author, or directly to Phrack Magazine).
-
- To prevent these sorts of shenanigans from happening remotely across the
- Internet, the concerned system administrator can block NBT traffic at the
- gateway (this sort of traffic should not be allowed to/from the Internet as
- standard fare). If you are running NT 4.0, install the service packs, and set
- the appropriate registry values to disable the attack. Or use OpenBSD.
-
-
- ----[ THE CODE
-
-
- QTIP has a few options. qtip -h supplies the following info:
-
- usage qtip[asug<username>hv] <target>
- -s: get share list
- -u: get user list
- -g <username>: get infos about <username>
- -d: leave connection established on exit
- -a: -s + -u
- -h, -?: display this help
- -v: be verbose (use twice to be garrulous)
-
- Seems rather self explanatory. If the verbose flag is set, then -u
- implies a recursive -g. -d is handy if you plan to take a look at the
- registry as well (there's gold in them thar hills). Omission of all flags just
- establishes a null session and exits. <target> can be a fully-qualified
- domain name, ip address, or UNC format. The code compiles like a dream under
- visual c 4.1. There is no makefile included, just link the code against
- kernel32.lib, libc.lib and wsock32.lib. This program is most useful wrapped
- in scripts with something like tping(ip sweeper), and maybe a few registry
- inquisition perl scripts. Feel free to redistribute, just give props where
- props are due, and please let me know if you make any interesting changes.
-
- <++> qtip/qtip.h
- /*
- * qtip.h
- * 12/04/1997
- * twitch
- * twitch@aye.net
- *
- * a quick nt investigative probe. (mis)uses null sessions to collect
- * sundry information about a WindowsNT server. distribute as you
- * please. be alert, look alive, and act like you kow.
- *
- * '...i should dismiss him, in order to teach him that pleasure consists
- * not in what i enjoy, but in having my own way.'
- * -sk, either/or
- */
-
- #include <stdio.h>
- #include <windows.h>
- #include <winsock.h>
- #include "lm.h"
-
- #define k16 16384
- #define TARG_LEN 255
- #define USER_LEN 22
-
- void handle_error(DWORD);
- void prepend_str(char *, char*);
- int open_session();
- int procure_userlist();
- int procure_sharelist();
- void parse_cl(int, char **);
- void usage(char *);
- int powerup(int, char **);
- void bail(const char *);
- int close_session();
- void get_usr_info(wchar_t *);
-
- /* couple o globals to make my life easier */
- u_int OPT_SHARES, OPT_USERS, OPT_GETUI;
- u_int OPT_NODEL, VERB;
- char target[TARG_LEN];
- WCHAR utarg[TARG_LEN];
- WCHAR user[USER_LEN];
- NETRESOURCE nr;
-
- <-->
- <++> qtip/qtip.c
-
- /*
- * qtip.c
- * 10/04/1997
- * twitch
- * twitch@aye.net
- *
- * a quick nt investigative probe
- * link against kernel32.lib, libc.lib and wsock32.lib.
- * qtip -h for usage. distribute as you please.
- *
- */
-
- #include "qtip.h"
-
- int main(int argc, char *argv[])
- {
- if( (powerup(argc, argv)) )
- return(1);
-
- if( (open_session()) != 0)
- return(1);
-
- if(OPT_SHARES)
- procure_sharelist();
-
- if(OPT_USERS)
- procure_userlist();
-
- if(OPT_GETUI)
- get_usr_info(utarg);
-
- close_session();
- return(0);
- }
-
- int open_session()
- {
- DWORD r;
-
- nr.dwType = RESOURCETYPE_ANY;
- nr.lpLocalName = NULL;
- nr.lpProvider = NULL;
- nr.lpRemoteName = target;
-
- if(VERB)
- printf("establishing null session with %s...\n", target);
-
- r = WNetAddConnection2(&nr, "", "", 0);
- if(r != NO_ERROR){
- handle_error(r);
- return -1;
- }
-
- if(VERB)
- printf("connection established\n");
-
- return 0;
- }
-
- /*
- * procure_userlist()
- * just use the old lm NetUserEnum() because there isnt comparable
- * functionality in the WNet sect. i just wish the win32 api was
- * more bloated and obtuse.
- */
- int procure_userlist()
- {
- NET_API_STATUS nas;
- LPBYTE *buf = NULL;
- DWORD entread, totent, rhand;
- DWORD maxlen = 0xffffffff;
- USER_INFO_0 *usrs;
- unsigned int i;
- int cc = 0;
-
- entread = totent = rhand = nas = 0;
- if( (buf = (LPBYTE*)malloc(k16)) == NULL)
- bail("malloc probs\n");
-
- if(VERB)
- wprintf(L"\ngetting userlist from %s...\n", utarg);
-
- nas = NetUserEnum(utarg, 0, 0, buf, maxlen, &entread, &totent, &rhand);
- if(nas != NERR_Success){
- fprintf(stderr, "couldnt enum users, ");
- handle_error(nas);
- goto cleanup;
- }
-
- cc = sizeof(USER_INFO_0) * entread;
- if( (usrs = (USER_INFO_0 *)malloc(cc)) == NULL){
- fprintf(stderr, "malloc probs\n");
- goto cleanup;
- }
-
- memcpy(usrs, *buf, cc);
- for(i = 0; i < entread; i++){
- wcscpy(user, usrs[i].usri0_name);
- wprintf(L"%s\n", user);
- if(VERB)
- get_usr_info(utarg);
- }
-
- cleanup:
- if(buf)
- free(buf);
-
- return 0;
- }
-
- /*
- * get_user_info()
- * attempt to gather some interesting facts about
- * a user
- */
- void get_usr_info(LPWSTR utarg)
- {
- NET_API_STATUS nas;
- USER_INFO_1 usrinfos;
- LPBYTE *buf = NULL;
-
- if( !(buf = (LPBYTE *)malloc(sizeof(USER_INFO_1))) )
- bail("malloc probs\n");
-
- nas = NetUserGetInfo(utarg, user, 1, buf);
-
- if(nas){
- fwprintf(stderr, L"couldnt get user info for for %s, ", user);
- handle_error(nas);
- }
- else{
- memcpy(&usrinfos, *buf, sizeof(USER_INFO_1));
-
- /* most of these will never happen, but nothings lost trying */
- if( (UF_PASSWD_NOTREQD & usrinfos.usri1_flags) )
- printf("\t-password not required, how about that.\n");
- if( (UF_ACCOUNTDISABLE & usrinfos.usri1_flags) )
- printf("\t-account disabled\n");
- if( (UF_LOCKOUT & usrinfos.usri1_flags) )
- printf("\t-account locked out\n");
- if( (UF_DONT_EXPIRE_PASSWD & usrinfos.usri1_flags) )
- printf("\t-password doesnt expire\n");
- if( (UF_PASSWD_CANT_CHANGE & usrinfos.usri1_flags) )
- printf("\t-user cant change password\n");
- if( (UF_WORKSTATION_TRUST_ACCOUNT & usrinfos.usri1_flags) )
- printf("\t-account for some other box in this domain\n");
- if( (UF_SERVER_TRUST_ACCOUNT & usrinfos.usri1_flags) )
- printf("\t-account for what is prolly the BDC\n");
- if( (UF_INTERDOMAIN_TRUST_ACCOUNT & usrinfos.usri1_flags) )
- printf("\t-interdomain permit to trust account\n");
- }
-
- free(buf);
- }
-
- /*
- * procure_sharelist()
- * strangely enough, this retrieves a sharelist from target
- */
- int procure_sharelist()
- {
- DWORD r;
- DWORD bufsize = 16384, cnt = 0xFFFFFFFF;
- HANDLE enhan;
- void *buf;
- NETRESOURCE *res;
- u_int i;
-
- if( (buf = malloc(bufsize)) == NULL){
- fprintf(stderr, "malloc probs, bailing\n");
- return -1;
- }
-
- nr.dwScope = RESOURCE_CONNECTED;
- nr.dwType = RESOURCETYPE_ANY;
- nr.dwDisplayType = 0;
- nr.dwUsage = RESOURCEUSAGE_CONTAINER;
- nr.lpLocalName = NULL;
- nr.lpRemoteName = (LPTSTR)target;
- nr.lpComment = NULL;
- nr.lpProvider = NULL;
-
- r = WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_ANY,
- RESOURCEUSAGE_CONNECTABLE, &nr
- , &enhan);
- if(r != 0){
- free(buf);
- printf("open_enum failed, sorry- ");
- handle_error(r);
- return -1;
- }
-
- r = WNetEnumResource(enhan, &cnt, buf, &bufsize);
- if(r != 0){
- free(buf);
- printf("enum_res failed- ");
- handle_error(r);
- return -1;
- }
-
- res = (NETRESOURCE*)malloc(cnt * sizeof(NETRESOURCE));
- if(res == NULL){
- free(buf);
- printf("malloc probs, i wont be listing shares.\n");
- return -1;
- }
- memcpy(res, buf, (cnt * sizeof(NETRESOURCE)) );
-
- for(i = 0; i < cnt; i++){
- if(VERB)
- printf("\nshare name:\t");
-
- printf("%s\n", res[i].lpRemoteName);
- if(VERB){
- printf("share type:\t");
- if(res[i].dwType = RESOURCETYPE_DISK)
- printf("disk");
- else
- printf("printer");
- printf("\ncomment:\t%s\n", res[i].lpComment);
- }
- }
-
- free(buf);
- free(res);
- return 0;
- }
-
- /*
- * close_session()
- * clean up our mess
- */
- int close_session()
- {
- DWORD r;
-
- WSACleanup();
- if(!OPT_NODEL)
- r = WNetCancelConnection2(target, 0, TRUE);
-
- if(r != 0){
- fprintf(stderr, "couldnt delete %s, returned %d\n", target, r);
- return -1;
- }
- else{
- if(VERB)
- printf("connection to %s deleted\n", target);
- }
-
- return 0;
- }
-
- /*
- * handle_error()
- * util function to deal with some errors.
- */
- void handle_error(DWORD err)
- {
- switch(err){
- case ERROR_ACCESS_DENIED:
- fprintf(stderr, "access is denied.\n");
- break;
- case ERROR_BAD_NET_NAME:
- fprintf(stderr, "bad net name.\n");
- break;
- case ERROR_EXTENDED_ERROR:
- fprintf(stderr, "an extended error occurred.\n");
- break;
- case ERROR_INVALID_PASSWORD:
- fprintf(stderr, "invalid password.\n");
- break;
- case ERROR_LOGON_FAILURE:
- fprintf(stderr, "bad username or password.\n");
- break;
- case NO_ERROR:
- fprintf(stderr, "it worked\n");
- break;
- case ERROR_BAD_NETPATH:
- fprintf(stderr, "network path not found.\n");
- break;
- default:
- fprintf(stderr, "a random error occurred (%d).\n", err);
- }
- }
-
- /*
- * prepend_str()
- * util funk to prepend chars to a string
- */
- void prepend_str(char *orgstr, char *addthis)
- {
- orgstr = _strrev(orgstr);
- addthis = _strrev(addthis);
- strcat(orgstr, addthis);
- orgstr = _strrev(orgstr);
- }
- /*
- * parse_cl()
- * try and make sense of the command line. no, i dont have a win32 getopt.
- * yes, i know i should
- */
- void parse_cl(int argc, char **argv)
- {
- int i, cc;
- char opt;
- DWORD r;
-
- OPT_SHARES = OPT_USERS = VERB = 0;
-
- for(i = 1; i < (argc); i++){
- if( (*argv[i]) == '-'){
- opt = *(argv[i]+1);
- switch(opt){
- case 'a':
- OPT_SHARES = 1;
- OPT_USERS = 1;
- break;
- case 's':
- OPT_SHARES = 1;
- break;
- case 'u':
- OPT_USERS = 1;
- break;
- case 'g':
- OPT_GETUI = 1;
- if( (strlen(argv[i+1])) > USER_LEN)
- bail("username too long (must be < 21)");
- ZeroMemory(user, USER_LEN);
- cc = strlen(argv[++i]);
- r = MultiByteToWideChar(CP_ACP, 0, argv[i], cc, user, (cc + 2));
- break;
- case 'd':
- om the Internet as
- stand break;
- case 'v':
- VERB++;
- break;
- default:
- if( (opt != 'h') && (opt != '?') )
- fprintf(stderr, "unknown option '%c'\n", opt);
- usage(argv[0]);
- break;
- }
- }
- }
-
- if( (OPT_SHARES) && (VERB) )
- printf("listing shares\n");
- if( (OPT_USERS) && (VERB) )
- printf("listing users\n");
- if( (OPT_GETUI) && (VERB) )
- wprintf(L"getting infos about user %s\n", user);
- if(VERB)
- printf("verbosity = %d\n", VERB);
- }
-
- /*
- * powerup()
- * just init stuff and parse the command line
- */
- int powerup(int argc, char **argv)
- {
- struct hostent *hent;
- u_long addie;
- WORD werd;
- WSADATA data;
- char buf[256];
- int cc = 0, ucc = 0;
-
- if(argc < 3)
- usage(argv[0]);
-
- parse_cl(argc, argv);
- ZeroMemory(buf, 256);
- strcpy(buf, argv[argc - 1]);
-
- /* if not unc format get the ip */
- if(buf[0] != '\\'){
- if(VERB > 1)
- printf("target not in unc\n");
-
- werd = MAKEWORD(1, 1);
- if( (WSAStartup(werd, &data)) !=0 )
- bail("couldnt init winsock\n");
-
- hent = (struct hostent *)malloc(sizeof(struct hostent));
- if(hent == NULL)
- bail("malloc probs\n");
-
- if( (addie = inet_addr(buf)) == INADDR_NONE){
- hent = gethostbyname(buf);
- if(hent == NULL){
- fprintf(stderr, "fatal: couldnt resolve %s.\n", buf);
- return -1;
- }
- ZeroMemory(buf, 256);
- strcpy(buf, inet_ntoa(*(struct in_addr *)*hent->h_addr_list));
- }
- prepend_str(buf, "\\\\");
- }
- else
- fprintf(stderr, "target already in unc\n");
-
- if( (strlen(buf) > (TARG_LEN - 1)) ){
- free(buf);
- bail("hostname too long (must be < 255 chars.)");
- return -1;
- }
-
- ZeroMemory(target, TARG_LEN);
- strcpy(target, buf);
-
- ZeroMemory(utarg, TARG_LEN);
- cc = strlen(target);
- ucc = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, target, cc, utarg, cc);
- if(ucc < 1){
- bail("unicode conversion probs, sorry");
- return -1;
- }
-
- return 0;
- }
-
- void usage(char *prog)
- {
- fprintf(stderr, "usage: %s [asug<username>hv] <target>\n", prog);
- fprintf(stderr, "\t-s:\t\tget share list\n");
- fprintf(stderr, "\t-u:\t\tget user list\n");
- fprintf(stderr, "\t-g: <username>\tget infos about just <username>\n");
- fprintf(stderr, "\t-d:\t\tleave connection established on exit\n");
- fprintf(stderr, "\t-a:\t\t-s + -u\n");
- fprintf(stderr, "\t-h, -?:\t\tdisplay this help\n");
- fprintf(stderr, "\t-v:\t\tbe verbose (use twice to be garrolous)\n");
- exit(0);
- }
-
- /*
- * bail()
- * just whine and die
- */
- void bail(const char *msg)
- {
- fprintf(stderr, "fatal: %s\n", msg);
- close_session();
- exit(1);
- }
- <-->
-
-
- ----[ EOF
-
-