Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

chroot && chdir

55 views
Skip to first unread message

Ulisses Alonso Camaro

unread,
Feb 13, 1998, 3:00:00 AM2/13/98
to

-----BEGIN PGP SIGNED MESSAGE-----


Hi all!


I found in two applications differs to setup a chrooted environment, both
application's authors have good reputation (one is wu-ftpd and the author of
the other is Wietse Venema)...

In Wietse's app chdir("/directory will be newroot") is executed after chroot

And in wu-ftpd, is executed chroot and chdir "/" secuentially

It seems to me that both ways are correct, isn't it?
Anybody can explain me why $PWD must to be changed in order to avoid
accessing and out of the new chrooted environment directory?

Thanks in advance,

Ulisses

PD: please send me a CC to uli...@pusa.uv.es


-----BEGIN PGP SIGNATURE-----
Version: 2.6.3a
Charset: latin1
Comment: PGP public key avaliable at http://www.rediris.es/cert/keyserver

iQB1AwUBNORT/A/N+5+NQ63pAQF21wL/YE3aRvJBDCysqXk0fy8tKHZNIzHu0cCI
gyZFSS+svOrJ0SC3ePrj/EXPG+tN/87rJeN0SmnWJRq57Gni9d4spHx/b/MsBdKe
w+hDbwRwNZKFtBDjQPegYKC+tjTTxCyz
=1cBK
-----END PGP SIGNATURE-----

Thomas H. Ptacek

unread,
Feb 13, 1998, 3:00:00 AM2/13/98
to

13 Feb 1998 14:01:32 GMT uli...@pusa.eleinf.uv.es:

>In Wietse's app chdir("/directory will be newroot") is executed after chroot
>And in wu-ftpd, is executed chroot and chdir "/" secuentially

I can't quite understand what you mean by this, but the simple rule is
that chroot() doesn't modify your current directory, so you should always
follow up a chroot() with a chdir() into the root directory.

You can do this by referring to the root directory directly, or by
explicitly naming the new root directory. The latter (ie, setting new root
to "/tmp", doing chroot("/tmp"), and then chdir("/tmp") is dangerous; if
something happens that causes the directory used by chroot() and chdir()
to be inconsistant, the chdir() won't cause chroot to be enforced. This is
a nit, though.

>Anybody can explain me why $PWD must to be changed in order to avoid
>accessing and out of the new chrooted environment directory?

In general terms, POSIX says that chroot() doesn't modify the current
working directory. This means that if you chroot() to a directory UNDER
your current directory (ie, you're in "/usr" and chroot yourself to
"/usr/local"), you are then "above" the root directory.

In 4.4BSD, this is a problem because of the way chroot() is enforced.
4.4BSD enforces chroot() in the VFS pathname lookup code (lookup(), which
is called from namei()). Chroot() is enforced in part by tracking pathname
components that refer to ".."; when a pathname lookup involves going up a
directory, the process' current root directory is checked against the
"current directory" lookup() is at. When that directory is the same the
root directory, lookup() stops going up directories.

For instance, if your current working directory is "/usr/local/home/foo",
and your root directory is "/usr/local", and you try to chdir to
"../../..", the following will happen:

Check if this is root dir. No? Keep going.
Lookup ".." (/usr/local/home).
Check if this is root dir. No? Keep going.
Lookup ".." (/usr/local)
Check if this is root dir. Yes? Stop honoring "..".
Lookup ".." as if it was "." (/usr/local)

However, if your root directory is instead "/usr/local/home/foo" and
you're in "/usr/local" (switching root and current from the last example),
and you chdir to "../..", this will happen:

Check if this is root dir. No? Keep going.
Lookup ".." (/usr)
Check if this is root dir. No? Keep going.
Lookup ".." (/)
Check if this is root dir. No? Keep going.

Even though you're chroot()'ed to /usr/local/home/foo, you have
unrestricted filesystem access. The problem is that if you're "above"
root, none of the directories you can ascend into will ever equal the root
directory, and the lookup() check will fail to notice that you're
chdir()'ing further and further away from the root directory. Of course,
as soon as you chdir() somewhere under the root directory, you're trapped
--- lookup() will catch ".." path components that go past the root
directory, because it will iterate into the root directory as it walks the
path.

There is a particularly slick bug in some recent revisions of 4.4BSD which
allows you to escape any chroot (as long as you're root). Assume you have
been properly contained in "/usr/local/home/foo" by:

chroot("/usr/local/home/foo");
chdir("/");

To escape chroot, create a new directory and chroot() to it without
chdir()'ing into it:

mkdir("bar");
chroot("/bar");

The 4.4BSD process table has now replaced it's notion of the root
directory with "/usr/local/home/foo/bar", and forgotten that the old
chroot ever existed. This shouldn't be a problem, since you can only
chroot() into directories UNDER the root directory as soon as you're
properly chroot()'d (in other words, you can only make your chroot()
restriction MORE restrictive).

Except that it is, because until you chdir() into the new root directory,
you're "above root". This means you can chdir() to any directory in the
filesystem using ".."'s, as in the example above.

OpenBSD fixed this by saying that as soon as you're chroot()'d, future
chroot() calls will automatically change your directory to "/". FreeBSD
appears to have fixed this by saying that chroot() always changes your
directory to "/".

---
-----------------------------------------------------------------------------
Thomas H. Ptacek Secure Networks, Inc.
-----------------------------------------------------------------------------
http://www.enteract.com/~tqbf "mmm... sacrilicious"

Theo de Raadt

unread,
Feb 13, 1998, 3:00:00 AM2/13/98
to

tq...@joshua.enteract.com (Thomas H. Ptacek) writes:

> There is a particularly slick bug in some recent revisions of 4.4BSD which
> allows you to escape any chroot (as long as you're root). Assume you have
> been properly contained in "/usr/local/home/foo" by:
>
> chroot("/usr/local/home/foo");
> chdir("/");

I bet this bug exists in every Unix, because of how the VFS layer works.
And I'd love to hear otherwise.

--
This space not left unintentionally unblank. der...@openbsd.org
www.OpenBSD.org -- We're fixing security problems so you can sleep at night.
(If it wasn't so fascinating I might get some sleep myself...)

Julie Haugh

unread,
Mar 9, 1998, 3:00:00 AM3/9/98
to

In article <cen17n...@zeus.theos.com>,

Theo de Raadt <der...@zeus.theos.com> wrote:
>tq...@joshua.enteract.com (Thomas H. Ptacek) writes:
>
>> There is a particularly slick bug in some recent revisions of 4.4BSD which
>> allows you to escape any chroot (as long as you're root). Assume you have
>> been properly contained in "/usr/local/home/foo" by:
>>
>> chroot("/usr/local/home/foo");
>> chdir("/");
>
>I bet this bug exists in every Unix, because of how the VFS layer works.
>And I'd love to hear otherwise.

As root you can do just about whatever you want to get out of a
chroot'd environment -- chroot() is a hack at best.

Remember that root can create device nodes, read and write physical
devices, read and write kernel memory, etc. Re-writing the u.u_rdir
(or equivalent) is just about all that is needed to hack a chroot'd
environment. And as root that's a pretty trivial little C program,
even if you have to do a mknod() or two first ...
--
Julianne Frances Haugh
RS/6000 Security Development, C2 Tech Lead "Resistance is futile!
Bldg 905/2F002, 512-823-8817 (Tie 793) You will be evaluated!"
I-net: j...@austin.ibm.com -- C2 of Borg

Thomas H. Ptacek

unread,
Mar 10, 1998, 3:00:00 AM3/10/98
to

9 Mar 1998 22:09:00 GMT j...@austin.ibm.com:

>Remember that root can create device nodes, read and write physical
>devices, read and write kernel memory, etc. Re-writing the u.u_rdir

You're going to have to get creative when I do:

mknod(struct proc *(p, void *uap, int *retval) {

...

if(p->p_fd->fd_rdir)
return(EACCESS);

...
}

(i.e., if we're chrooted [our process root directory pointer is not NULL
and thus not implicitly pointing to the systemwide root], don't allow this
operation.)

Note how simple that check is on 4.4BSD. You could always just do a
macro:

#define ISCHROOTED(x) ((x)->p_fd->fd_rdir != NULL)

Suitable for slapping in all over the kernel.

If you can't create a new device, you can't (trivially) read kernel
memory. Even if you could, you can't necessarilly write it, even if you're
root. That's besides the point, but it does address the claim that root
can trivially break chroot() by changing u-area pointers.

I agree that chroot() is a pitiful security mechanism when the process
being contained is "root". I disagree that it's a "hack"; I just think
it's capabilities haven't been documented well enough. It's certainly
useful as a mechanism for simplifying code.

In addition, I don't think chroot() is something that should be
deprecated; I think the methods by which it can be bypassed by root should
be exposed and dealt with. I don't know if you're making this point or
not.

--

Malcolm Beattie

unread,
Mar 10, 1998, 3:00:00 AM3/10/98
to

In article <slrn6g9i1...@joshua.enteract.com>,

Thomas H. Ptacek <tq...@secnet.com> wrote:
>9 Mar 1998 22:09:00 GMT j...@austin.ibm.com:
>>Remember that root can create device nodes, read and write physical
>>devices, read and write kernel memory, etc. Re-writing the u.u_rdir
>
>You're going to have to get creative when I do:
>
> mknod(struct proc *(p, void *uap, int *retval) {
>
> ...
>
> if(p->p_fd->fd_rdir)
> return(EACCESS);

Use ptrace to attach to any non-chrooted root process and write your
code into its address space for it to perform. Nailed that down yet?
I haven't checked the semantics of BSD ptrace.

--Malcolm

--
Malcolm Beattie <mbea...@sable.ox.ac.uk>
Oxford University Computing Services
"I permitted that as a demonstration of futility" --Grey Roger

Julie Haugh

unread,
Mar 10, 1998, 3:00:00 AM3/10/98
to

Thomas H. Ptacek wrote:
> In addition, I don't think chroot() is something that should be
> deprecated; I think the methods by which it can be bypassed by root should
> be exposed and dealt with. I don't know if you're making this point or
> not.

I was pointing out that a suitably imaginative person can find
aways out of chroot(). All of my experience with chroot() tells
me that the environment should be minimal and non-root. Anything
else leaves too many possibilities for a suitably creative person
to break out.
--
Julianne Frances Haugh Life is either a daring adventure
Mail: jfh AT bga.com or nothing at all.
-- Helen Keller

Thomas H. Ptacek

unread,
Mar 10, 1998, 3:00:00 AM3/10/98
to

10 Mar 1998 13:16:40 GMT mbea...@sable.ox.ac.uk:

>Use ptrace to attach to any non-chrooted root process and write your
>code into its address space for it to perform. Nailed that down yet?
>I haven't checked the semantics of BSD ptrace.

Yep. This is my pet chroot() problem; ptrace() (and procfs) is the general
problem of "process control" that I refer to frequently when talking
about chroot() security. Anyone who thinks this is difficult to exploit or
impractical: we have exploit code, and it's likely that the black-hats do
too.

Of course, it's easily addressed with ISCHROOTED() in the kernel. The
problem is that the kernel needs an audit.

Thomas H. Ptacek

unread,
Mar 10, 1998, 3:00:00 AM3/10/98
to

Tue, 10 Mar 1998 08:45:53 -0600 j...@bga.com:

>aways out of chroot(). All of my experience with chroot() tells
>me that the environment should be minimal and non-root. Anything
>else leaves too many possibilities for a suitably creative person
>to break out.

Since I have the luxury of freely available source code for my preferred
platform (4.4BSD), my approach is slightly different: I am working on
improving the kernel (via code audit and enhanced access control) so that
chroot() is more meaningful when containing root(). Working code
that we have right now allows you to sysctl (set kernel variables) off
arbitrary system calls from chroot().

Certainly, it's wise to tell people not to contain "root" in chroot(). I'm
just more interested in fixing it so that this isn't a limitation.

Julie Haugh

unread,
Mar 10, 1998, 3:00:00 AM3/10/98
to

In article <slrn6gaqm...@joshua.enteract.com>,

Thomas H. Ptacek <tq...@secnet.com> wrote:
>10 Mar 1998 13:16:40 GMT mbea...@sable.ox.ac.uk:
>>Use ptrace to attach to any non-chrooted root process and write your
>>code into its address space for it to perform. Nailed that down yet?
>>I haven't checked the semantics of BSD ptrace.
>
>Yep. This is my pet chroot() problem; ptrace() (and procfs) is the general
>problem of "process control" that I refer to frequently when talking
>about chroot() security. Anyone who thinks this is difficult to exploit or
>impractical: we have exploit code, and it's likely that the black-hats do
>too.

Oh, it's a snap. Troy Bollinger (he sits down the hall from me) and
I were discussing this a few minutes ago and I mentioned a program I'd
written for AIX some time back -- "gcore". It basically does what
the SunOS command by the same name does, except it uses ptrace() because
AIX lacks other facilities for doing it.

>Of course, it's easily addressed with ISCHROOTED() in the kernel. The
>problem is that the kernel needs an audit.

Except that this problem can also be used for non-chroot()'d situations.
Like in.ftpd ...

Tim Newsham

unread,
Mar 11, 1998, 3:00:00 AM3/11/98
to

Julie Haugh (j...@austin.ibm.com) wrote:
: Remember that root can create device nodes, read and write physical

: devices, read and write kernel memory, etc. Re-writing the u.u_rdir
: (or equivalent) is just about all that is needed to hack a chroot'd

: environment. And as root that's a pretty trivial little C program,
: even if you have to do a mknod() or two first ...

I agree that chroot is easy to get out of. But on some systems
it is a bit harder than just mknod'ing a file and editing away.
Some systems wont let you edit a device thats mounted as a filesystem
while in a secure mode, and some don't let you do it ever.

So you might have to break securelevels or hack the kernel in
some way if you want to take the mknod + edit filesystem route.

: Julianne Frances Haugh
: I-net: j...@austin.ibm.com -- C2 of Borg

Tim N.

Message has been deleted
0 new messages