Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 4 Dec 2011 19:18:25 +0200
From:      Kostik Belousov <kostikbel@gmail.com>
To:        joris dedieu <joris.dedieu@gmail.com>
Cc:        freebsd-hackers <freebsd-hackers@freebsd.org>
Subject:   Re: rtld and noexec
Message-ID:  <20111204171825.GE50300@deviant.kiev.zoral.com.ua>
In-Reply-To: <CAPd55qD%2B7PQ6vkc27%2BxQeeehJwCMG8x%2BqBxZ7sTit4i1M_yotQ@mail.gmail.com>
References:  <CAPd55qAoCttEbV8fkALDaFBc60jigfbu9=Uwa_%2BaECYaHFJpSw@mail.gmail.com> <20111202164157.3058d91d@kan.dyndns.org> <CAPd55qD%2B7PQ6vkc27%2BxQeeehJwCMG8x%2BqBxZ7sTit4i1M_yotQ@mail.gmail.com>

next in thread | previous in thread | raw e-mail | index | archive | help

--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 <kabaev@gmail.com>:
> > On Fri, 2 Dec 2011 18:22:57 +0100
> > joris dedieu <joris.dedieu@gmail.com> 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--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20111204171825.GE50300>