From owner-freebsd-hackers@FreeBSD.ORG Sun Dec 4 17:18:30 2011 Return-Path: Delivered-To: freebsd-hackers@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 1F3EB106564A for ; Sun, 4 Dec 2011 17:18:30 +0000 (UTC) (envelope-from kostikbel@gmail.com) Received: from mail.zoral.com.ua (mx0.zoral.com.ua [91.193.166.200]) by mx1.freebsd.org (Postfix) with ESMTP id 7E79D8FC1D for ; Sun, 4 Dec 2011 17:18:29 +0000 (UTC) Received: from alf.home (alf.kiev.zoral.com.ua [10.1.1.177]) by mail.zoral.com.ua (8.14.2/8.14.2) with ESMTP id pB4HIPvs025374 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Sun, 4 Dec 2011 19:18:25 +0200 (EET) (envelope-from kostikbel@gmail.com) Received: from alf.home (kostik@localhost [127.0.0.1]) by alf.home (8.14.5/8.14.5) with ESMTP id pB4HIPSW098797; Sun, 4 Dec 2011 19:18:25 +0200 (EET) (envelope-from kostikbel@gmail.com) Received: (from kostik@localhost) by alf.home (8.14.5/8.14.5/Submit) id pB4HIPN2098796; Sun, 4 Dec 2011 19:18:25 +0200 (EET) (envelope-from kostikbel@gmail.com) X-Authentication-Warning: alf.home: kostik set sender to kostikbel@gmail.com using -f Date: Sun, 4 Dec 2011 19:18:25 +0200 From: Kostik Belousov To: joris dedieu Message-ID: <20111204171825.GE50300@deviant.kiev.zoral.com.ua> References: <20111202164157.3058d91d@kan.dyndns.org> Mime-Version: 1.0 Content-Type: multipart/signed; micalg=pgp-sha1; protocol="application/pgp-signature"; boundary="vTAnc/q0mOWkBj2n" Content-Disposition: inline In-Reply-To: User-Agent: Mutt/1.4.2.3i X-Virus-Scanned: clamav-milter 0.95.2 at skuns.kiev.zoral.com.ua X-Virus-Status: Clean X-Spam-Status: No, score=-3.9 required=5.0 tests=ALL_TRUSTED,AWL,BAYES_00 autolearn=ham version=3.2.5 X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on skuns.kiev.zoral.com.ua Cc: freebsd-hackers Subject: Re: rtld and noexec X-BeenThere: freebsd-hackers@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Technical Discussions relating to FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 04 Dec 2011 17:18:30 -0000 --vTAnc/q0mOWkBj2n Content-Type: text/plain; charset=koi8-r Content-Disposition: inline Content-Transfer-Encoding: quoted-printable On Sun, Dec 04, 2011 at 02:17:43PM +0100, joris dedieu wrote: > 2011/12/2 Alexander Kabaev : > > On Fri, 2 Dec 2011 18:22:57 +0100 > > joris dedieu wrote: > > > >> Hi, > >> > >> Here is a patch I use to prevent loading a shared object from a noexec > >> mountpoint. =9AIt's an easy way, I found, after the last root exploit > >> ((http://seclists.org/fulldisclosure/2011/Nov/452), =9Ato enhance =9At= he > >> security of my web servers (with /home, /tmp and /var/tmp mounted with > >> noexec). > >> > >> - the last ftpd/porftpd =9A(libc ?) exploit does not work (indirect use > >> of rtld via nsswitch) > >> - the previous rtld security issue should have been more difficult to > >> use in a noexec context. > >> - It may help to prevent some miscellaneous usage of common softwares > >> using dlopen like apache or php. > >> > >> I think it also makes sens because loading a shared object sounds like > >> a kind of "execution". > >> > >> What do you think about this patch and the opportunity to open a PR on > >> this subject? > >> > >> Cheers > >> Joris > >> > >> > >> --- libexec/rtld-elf/rtld.c.orig =9A =9A =9A =9A2011-12-02 12:09:40.00= 0000000 > >> +0100 +++ libexec/rtld-elf/rtld.c =9A =9A 2011-12-02 13:45:18.000000000 > >> +0100 @@ -1123,32 +1123,50 @@ > >> =9A{ > >> =9A =9A =9Achar *pathname; > >> =9A =9A =9Achar *name; > >> + =9A =9Astruct statfs mnt; > >> > >> =9A =9A =9Aif (strchr(xname, '/') !=3D NULL) { =9A/* Hard coded pathna= me */ > >> + =9A =9A =9Aname =3D NULL; > >> =9A =9A =9A =9A if (xname[0] !=3D '/' && !trust) { > >> =9A =9A =9A =9A =9A =9A _rtld_error("Absolute pathname required for sh= ared object > >> \"%s\"", xname); > >> =9A =9A =9A =9A =9A =9A return NULL; > >> =9A =9A =9A =9A } > >> =9A =9A =9A =9A if (refobj !=3D NULL && refobj->z_origin) > >> - =9A =9A =9A =9A =9A return origin_subst(xname, refobj->origin_path); > >> + =9A =9A =9A =9A =9A pathname =3D origin_subst(xname, refobj->origin_= path); > >> =9A =9A =9A =9A else > >> - =9A =9A =9A =9A =9A return xstrdup(xname); > >> + =9A =9A =9A =9A =9A pathname =3D xstrdup(xname); > >> + =9A =9A} > >> + =9A =9Aelse { /* xname is not a path */ > >> + =9A =9A =9A if (libmap_disable || (refobj =3D=3D NULL) || > >> + =9A =9A =9A =9A =9A (name =3D lm_find(refobj->path, xname)) =3D=3D N= ULL) > >> + =9A =9A =9A =9A =9A name =3D (char *)xname; > >> + > >> + =9A =9A =9A dbg(" Searching for \"%s\"", name); > >> + > >> + =9A =9A =9A pathname =3D search_library_path(name, ld_library_path); > >> + =9A =9A =9A if (pathname =3D=3D NULL && refobj !=3D NULL) > >> + =9A =9A =9A =9A =9A =9Apathname =3D search_library_path(name, refobj= ->rpath); > >> + =9A =9A =9A if (pathname =3D=3D NULL) > >> + =9A =9A =9A =9A =9A =9Apathname =3D search_library_path(name, gethin= ts()); > >> + =9A =9A =9A if (pathname =3D=3D NULL) > >> + =9A =9A =9A =9A =9A =9Apathname =3D search_library_path(name, > >> STANDARD_LIBRARY_PATH); > >> + =9A =9A} > >> + > >> + =9A =9Aif (pathname !=3D NULL) { /* noexec mountpoint in pathname */ > >> + =9A =9A =9A if (statfs(pathname, &mnt) !=3D 0) > >> + =9A =9A =9A =9A =9A =9Afree(pathname); > >> + =9A =9A =9A else { > >> + =9A =9A =9A =9A =9A =9Aif (mnt.f_flags & MNT_NOEXEC) { > >> + =9A =9A =9A =9A =9A =9A =9A_rtld_error("noexec violation for shared = object > >> \"%s\"", pathname); > >> + =9A =9A =9A =9A =9A =9A =9Afree(pathname); > >> + =9A =9A =9A =9A =9A =9A =9Areturn NULL; > >> + =9A =9A =9A =9A =9A =9A} > >> + =9A =9A =9A =9A =9A =9Aelse > >> + =9A =9A =9A =9A =9A =9A =9Areturn pathname; > >> + =9A =9A =9A } > >> =9A =9A =9A} > >> > >> - =9A =9Aif (libmap_disable || (refobj =3D=3D NULL) || > >> - =9A =9A =9A (name =3D lm_find(refobj->path, xname)) =3D=3D NULL) > >> - =9A =9A =9A name =3D (char *)xname; > >> - > >> - =9A =9Adbg(" Searching for \"%s\"", name); > >> - > >> - =9A =9Aif ((pathname =3D search_library_path(name, ld_library_path))= !=3D > >> NULL || > >> - =9A =9A =9A(refobj !=3D NULL && > >> - =9A =9A =9A(pathname =3D search_library_path(name, refobj->rpath)) != =3D NULL) > >> || > >> - =9A =9A =9A(pathname =3D search_library_path(name, gethints())) !=3D= NULL || > >> - =9A =9A =9A(pathname =3D search_library_path(name, > >> STANDARD_LIBRARY_PATH)) !=3D NULL) > >> - =9A =9A =9A return pathname; > >> - > >> =9A =9A =9Aif(refobj !=3D NULL && refobj->path !=3D NULL) { > >> =9A =9A =9A =9A _rtld_error("Shared object \"%s\" not found, required = by > >> \"%s\"", name, basename(refobj->path)); > >> _______________________________________________ > > > > > > 1. There is a race using statfs and then loading the file. > I will look at this point. Maybe statfs on the dirname ? >=20 > > 2. We already have the check in do_load_object > It doesn't work with dlopen. >=20 > mount |grep tank/t > tank/t on /tank/t (zfs, local, noexec, nfsv4acls) >=20 > so /tank/t is noexec >=20 > Here the powerful libmoo source code : >=20 > void say_moo() { > printf("mooooooooooooooooo\n"); > } >=20 > it's in /tank/t so noexec >=20 > ls -l /tank/t/ > total 6 > -rwxr-xr-x 1 joris joris 4632 Dec 4 13:52 libmoo.so >=20 > 1) First test with : >=20 > main() { > say_moo(); > } >=20 > LD_LIBRARY_PATH=3D/tank/t ./test_moo > /libexec/ld-elf.so.1: Cannot execute objects on /tank/t >=20 > Ok cool work has expected. >=20 > Second test with : >=20 > main() { > void * handle =3D dlopen("/tank/t/libmoo.so", RTLD_LAZY); > if (! handle) { > fprintf(stderr, "%s\n", dlerror()); > exit(1); > } > void (* moo) (void) =3D dlsym (handle, "say_moo"); > (* moo)(); > dlclose (handle); > } >=20 > ./test_moo > mooooooooooooooooo >=20 > Protection is not working when you use dlopen. This is what append > with ftpd exploit . libc just load a shared object and the guy is > root. If you started to change rtld code, it pays to read it first. The first example worked only because you used LD_LIBRARY_PATH. See r144062. It has nothing to do with dlopen/static linking. I do not think you patch is a right thing to do. noexec check is a hack anyway, because noexec _mount_ option is only there to globally disable execve(2) from the mount point, and not to disable mapping with PROT_EXEC. rtld does not provide any magic from the kernel POV, so purposive person can re-implement it at will in her code. --vTAnc/q0mOWkBj2n Content-Type: application/pgp-signature Content-Disposition: inline -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.18 (FreeBSD) iEYEARECAAYFAk7bq2EACgkQC3+MBN1Mb4ibhACbBNS8aLZJZjDoxbCTLH+tgRnQ 4U4An3t/Owe0k5gK3w1hwZm1/mrWjEyF =4WPM -----END PGP SIGNATURE----- --vTAnc/q0mOWkBj2n--