Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 20 Oct 2010 09:53:48 -0700
From:      Devin Teske <dteske@vicor.com>
To:        Julian Elischer <julian@freebsd.org>
Cc:        Pawel Jakub Dawidek <pjd@freebsd.org>, freebsd-rc@freebsd.org
Subject:   Re: sysrc(8) -- a sysctl(8)-like utility for managing rc.conf(5)
Message-ID:  <1287593628.19873.41.camel@localhost.localdomain>
In-Reply-To: <4CBE6F55.5040609@freebsd.org>
References:  <1286925182.32724.18.camel@localhost.localdomain> <1286996709.32724.60.camel@localhost.localdomain> <1287448781.5713.3.camel@localhost.localdomain> <1287510629.25599.2.camel@localhost.localdomain> <20101019195225.GB2127@garage.freebsd.pl> <1287540769.25599.73.camel@localhost.localdomain> <4CBE6F55.5040609@freebsd.org>

next in thread | previous in thread | raw e-mail | index | archive | help
On Tue, 2010-10-19 at 21:25 -0700, Julian Elischer wrote:
>   On 10/19/10 7:12 PM, Devin Teske wrote:
> > On Tue, 2010-10-19 at 21:52 +0200, Pawel Jakub Dawidek wrote:
> >> On Tue, Oct 19, 2010 at 10:50:29AM -0700, Devin Teske wrote:
> >>> I added `-j jail' for specifying a jail id or name to operate within
> >>> (requires jls(8); overrides `-R dir').
> >> [...]
> >>
> >> Note that operating on jail files from outside a jail is serious
> >> security problem. The files from within the jail can be symbolic links
> >> that point to files from outside a jail from your perspective.  Even
> >> chroot(2) to jail's root is neither safe (running applications that can
> >> be modified by jail's root is obvious security hole) nor reliable (jail
> >> might not have all the commands). The only safe way is to jexec(8) into
> >> a jail, but it of course has the same relialability issue as chroot(8).
> >>
> > I see the concern, and you're absolutely right.
> >
> > I did see the need to use either chroot(8) or jexec(8), but for exactly
> > the same reasons you mention (handing execution off-to something that
> > could have been modified by the jail's root-user), I ended up writing
> > routines that just edited the files outside the jail.
> >
> > It appears that (due to the other aforementioned security problem,
> > involving hand-crafted symbolic links with malicious-intent), the only
> > proper solution would be:
> >
> > a. Copy ourselves into some temporary location within the jail
> > b. Hand execution off to ourself using either jexec(8) or chroot(8)
> >
> can you fire off a shell in the jail and somehow shove yourself down 
> it's throat?
> I vaguely remember that one  could feed a shell script into the shell 
> somehow..
> but I forget the details.

You bet.

$ echo tail -2 /usr/include/osreldate.h | sudo chroot / /bin/sh
#define __FreeBSD_version 801000
#endif
$ echo tail -2 /usr/include/osreldate.h | sudo chroot /usr/jail/foomaster /bin/sh
#define __FreeBSD_version 492101
#endif

A more complete (yet simplified) example of a shell-script shoving
itself into a chroot'ed shell -- complete with custom-set of positional
parameters:

================================= BEGIN FILE: foo.sh
#!/bin/sh
jailed="$( sysctl -n security.jail.jailed )"
echo "jailed: $jailed"
echo -n "args: "
for arg in "$@"; do
        echo -n "'$arg' "
done
echo
[ $jailed -eq 1 ] && exit
( echo 'set -- "a  b" "c  d" "e  f"'
  cat $0
) | sudo jexec 8 /bin/sh
================================= END FILE

When executed:

$ uname -spr
FreeBSD 8.1-RELEASE-p1 amd64

$ jls jid path
8 /usr/jail/foomaster
9 /usr/jail/foomaster.deluxe

$ ./foo.sh "1  2" "3  4" "5  6"
jailed: 0
args: '1  2' '3  4' '5  6'
jailed: 1
args: 'a  b' 'c  d' 'e  f'

This is of-course a simplified example. A productionized version should
take into consideration that PATH expansion may have been performed.
This is trivial to "undo" as it were:

myscript="$0"
if [ ! -f "$0" ]; then
	# NOTE: `-x' should not be used because we could have been
	# invoked as `sh script' which allows execution of non-
	# executable shell-scripts

	# Perform reverse-PATH expansion
	for dir in $PATH; do
		# NOTE: If PATH-expansion was performed, the file
		# must now be executable (where earlier it could
		# have been non-executable if executed via sh in an
		# interactive shell)

		[ -x "$dir/$0" -a -f "$dir/$0" ] || continue

		# If we hit this point, we found a match

		myscript="$dir/$0"
		break
	done
fi

# At this point, $myscript should be a valid path to our own script
# regardless of whether we were invoked by any of the following:
#
# sh ./myscript         : Relative path (executable-bit optional)
# sh myscript           : Relative path (executable-bit optional)
# sh /path/to/myscript  : Absolute path (executable-bit optional)
# env sh myscript       : Relative path (executable-bit optional)
# ./myscript            : Relative path
# /path/to/myscript     : Absolute path
# myscript              : $PATH expansion performed

However, this method is not without pitfalls:

1. Let's say that the script being executed is /usr/local/bin/myscript
2. Let's say that this script was invoked using $PATH expansion ($0 ==
'myscript')
3. Let's say that the CWD is ~root
4. Let's say that `./myscript' exists
5. The above code will incorrectly think that we
(/usr/local/bin/myscript) are ~root/myscript

Unfortunately, there seems little way around this as even the following
code fails in interesting ways:

myscript="$0"
case "$myscript" in
*/*) : Do nothing (already an absolute or relative path)
     ;;
*)
	# $0 doesn't contain any slashes -- we were invoked in
	# one of two ways:
	#
	# 1. via $PATH expansion in the interactive shell
	# 2. via sh(1) and $0 truly is in the CWD
	#
	# NOTE: It is not possible to detect when we were invoked
	# via exec(3) versus sh(1) versus sh(1)-via-env(1) so
	# testing for #2 above is impossible.

	for dir in $PATH; do
		[ -x "$dir/$0" -a -f "$dir/$0" ] || continue
		myscript="$dir/$0"
		break
	done
esac


The comments in the above code-block explain why it won't work.

In the case of:

	sh myscript

$0 will equal "myscript" but, when reverse-PATH expansion is performed,
we may end up finding something with the same name and then end up
executing something perhaps entirely different.

It seems clear that the only safe way to pass off execution to onself is
to hard-code the path to yourself.

For example:

myscript=/usr/local/bin/foo
( echo 'set -- "a  b" "c  d" "e  f"'
  cat $myscript
) | sudo jexec 8 /bin/sh


Can anyone think of anything better?
-- 
Cheers,
Devin Teske

-> CONTACT INFORMATION <-
Business Solutions Consultant II
FIS - fisglobal.com
510-735-5650 Mobile
510-621-2038 Office
510-621-2020 Office Fax
909-477-4578 Home/Fax
devin.teske@fisglobal.com

-> LEGAL DISCLAIMER <-
This message  contains confidential  and proprietary  information
of the sender,  and is intended only for the person(s) to whom it
is addressed. Any use, distribution, copying or disclosure by any
other person  is strictly prohibited.  If you have  received this
message in error,  please notify  the e-mail sender  immediately,
and delete the original message without making a copy.

-> FUN STUFF <-
-----BEGIN GEEK CODE BLOCK-----
Version 3.1
GAT/CS d(+) s: a- C++(++++) UB++++$ P++(++++) L++(++++) !E--- W++ N? o? K- w O
M+ V- PS+ PE Y+ PGP- t(+) 5? X+(++) R>++ tv(+) b+(++) DI+(++) D(+) G+>++ e>+ h
r>++ y+ 
------END GEEK CODE BLOCK------
http://www.geekcode.com/

-> END TRANSMISSION <-




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