The fourth video talks
about something called Dynamic Loading.
Now this was developed in the sixties.
After the systems that used it then were retired,
it was forgotten until
about 1990 something on Linux systems.
Essentially dynamic loading, when a program runs,
it has stubs in it that
referred to functions in the library.
When the first function is called,
the stub pulls the contents
of the library into memory and
then transfers control to the function that was called.
This saves a lot of space on binaries and also it's
very nice in the sense that if
the function is upgraded in the library,
you'll get the latest one.
That's a huge plus,
but it can also be a huge minus.
Here's an example.
Let's say that a privileged program like /bin/login,
which is used when you log into
Linux, does dynamic loading,
and in this case it actually loaded fgets,
which is a program to read a line
of input from the keyboard,
of course it would read the username.
Well, what I do is,
since fgets is dynamically loaded,
I build my own version of fgets.
In this case, all that it's
going to do is spawn a sub shell.
I put it into a library called
libme.so in the current working directory.
When the program runs,
you assume that library functions
do what they're supposed to do,
and they're not changed.
Similarly, the programs assume
that when they call a library function,
it runs as the author of the program intended,
it does exactly what that author was intended.
But as I mentioned, dynamic loading
essentially has exactly the opposite intent.
Either load your own version of the function
or load the latest one from the current system library.
This next slide shows
exactly what I was talking about earlier.
Most systems will add the library to the heap,
not all of them, some of them have a special area.
But again, the stub will jump to
the actual function and
the instructions are rewritten so
that the next time it will just
branch directly to the function.
Okay. So the stub goes
out to find a library and pulls it in.
How does the stub figure out where to look?
Whenever you hear, it finds,
the question is well,
how? What does it look for?
In this case, it looks for
a couple of environment variables that are just like
pay-off except they were for
the libraries, LD_LIBRARY_PATH and LD_PRELOAD.
Don't worry about the difference between the two.
Other systems, by the way,
have environment variables that do the same thing.
But they're called beginning with ELF.
Now, sometimes these variables are ignored,
and where the programs look is
defined by the system and you can't change that.
I'll get to that in a moment.
On most systems, you do dynamic loading.
You can usually get around it.
The one that I've done that on
is called Solaris which is now
being phased out of no longer
supported, but it's painful.
On other systems, it's maybe less painful.
Configuration option for example.
You can also sometimes control this with
compiler flags and tell the compiler, "Look,
I want this program linked statically not
dynamically," and for anything security related,
I strongly recommend that for
reasons you'll see in a few minutes.
Okay. Going back to the attack, if you recall,
we were looking at bin/login
and we had defined our own version of fgets,
and libme.so in the current working directory.
I said the PRE_LOAD function to look in
the current working directory first and I run bin/login.
What happens is, PRE_LOAD looks in
the current directory for a library.
In this case, that so indicates it's
a dynamic library libme.so.
Then looks in the library to see
if the function is named there.
If it is it loads it, and executes it.
If it's not, then it goes on to the next library,
when it's done all the libraries in that directory,
it then goes on to the next directory in the path.
But in this case since bin/login is privileged,
I now have a privileged shell.
In terms of privilege,
something to think about is what
really is the problem here?
The problem is that I have
a privileged program but
it's trusting something an unprivileged user can alter,
like the search path for the library.
So in essence, this
violates what's called least privilege,
because lower privileged process b,
should never be able to alter a higher privileged one.
This, by the way, is standard insecurity.
There is a model called
by-bar which essentially says that
something with high integrity should never rely
on information that is of lower integrity.
Here the high integrity is the privileged program and
the low integrity is what
I put in my environment variables.
So this does violate
several very well-known rules and principles.
Okay. So how do you fix this?
The first one is okay.
I'm trusted. I'm a privileged program.
So I'm going to ignore anything that's unprivileged.
So what happens is when the program is set UID to root,
or being run by the administrator,
it simply ignores
the environment variables that tell it where to look
for functions and instead
uses a predefined set of directories.
That solves this problem entirely,
because the user can control what's
dynamically loaded in unprivileged programs,
in which case they might hurt
themselves or get access to their own account,
which is not a security violation.
But they can't get access,
they can't control what the privileged program does,
because even if they alter
their own environment variables
and then execute the privilege program,
it's not going to affect how the privileged program finds
these particular libraries and functions.
That, by the way, is what most systems do now.
When something is dynamically loaded,
if it's running with privileges,
it only looks in a predefined system set of directories.
This seems like you just solved the problem completely,
but then there's a little issue.
What happens if that setuid program
runs an unprivileged program?
Login does this by the way,
because it spawns the log in shell or
some other designated program that's not setuid to root.
When you login as sync, for example,
S-Y-N-C, on most systems,
that will simply leave flush data out to
the disks and then log you out instantly.
So, the non setuid program is running as someone else.
In this sync case, the user sync,
and it runs with the privileges of that user,
but it's not setuid.
Therefore, in this case,
it will go ahead and load my environment variables,
and that's exactly what
I'm going to show you as the next example.
People who are at login were smart.
By default, when login runs,
it deletes its current environment
and creates a new safe one.
But sometimes that's not useful.
For example, if Tom is a system's person and has
his own set of
environment variables tailored for a particular project,
if Karen wants to come in and use
his system using the same environment that Tom has,
Tom doesn't want her to sit down and use his account,
he wants her to log in as her.
But he also wants her to have access to
all of the environment variables he's defined.
So in that case, Tom doesn't log out.
Karen walks over to where Tom is,
and executes the login program with the dash p option.
That effectively logs Tom's out,
but it preserves environment for Karen.
So now, she has his
environment variables in the definitions.
Now, and what I'm going to show
you, I'm going to use sync,
but you can use any account
that you can complete login from.
If you can't complete the login,
this trick doesn't work.
The other reason I used
sync is because on
most systems it doesn't have a password,
it just allows you to do the flushing of the buffers.
Sync is a user that really is powerless.
But if you look at its group,
its group is typically daemon,
and that group has access
to all of the servers on the system.
It turns out to the login shell is bin sync,
because sync does not give you a command interpreter,
it simply runs the program,
sync to flush the buffers, and then quits.
It's a system call.
It rather relies on a system call.
That system call has
an interface which is dynamically loaded.
So in other words,
we're in exactly the situation I described earlier.
A privileged program root is
ignoring the environment as it runs some function.
It runs a non-privileged program, sync.
But login doesn't clean out its environment,
it just ignores those I values.
So, they pass down to sync.
Now, when sync runs,
it's going to look in that set of directories to
load functions, and that's the trick.
I do exactly what I did earlier with login in fgets,
except instead of calling it fgets, I call it sync,
and then I execute exactly the same sequence of
steps, and I'll get a shell.
The shell will not be a root shell.
So there are a lot of things I can't do.
But it will be a shell
with administrative group privileges,
and very often, things are group accessible, for example,
in one system that you know of,
the authentication database was writable by
the daemon group in order
to allow many different system administrators
to be able to alter the database if needed.
So on that system, having the shell,
I could simply go in and
myself with administrative privileges,
and then login, and then have administrator privileges.
So that's some of the things,
nastiness, that can happen.
This next slide describes exactly what happened.
Login ignores the preload and works as expected.
That's because it's setuid,
it's a program to do that.
But bin sync, which spawns is not setuid,
and so it uses the preload.
So this means, while I can't alter
the behavior of a setuid program,
I can alter the behavior of its children.
The way this was fixed on systems is that,
when a setuid program spawns any other program,
that program is tailored to
ignore any dynamic loading environment variables.
This typically happens in the kernel.
However, I wouldn't depend upon it.
The reason is that, not all systems do this and at
any way, aesthetically, it's back-patching.
What you really want to do is
delete the entire environment,
and then create a new known safe one.
If you can't do that, then if your program is running,
and going to spawn a sub command,
and your program is privileged,
make sure you delete the value of
those variables from your environment list.
Here's an example on
fairly old system that does
exactly this, what we talked about.
The LD program is
a program that loads something into
memory and it's called the dynamic loader.
This time, I'm just having it spawn
a sub shell, making it executable,
and I've set my path up,
so that path will hit dot first.
Then, what I do, is I simply run
a sequence of programs that
will dynamically load something into memory.
In this case, it's sd.o.
When the dynamic loading occurs,
it's going to invoke the loader,
ld, which automatically will
give me a command interpreter.
Since the load module program is privileged,
but it spawns an unprivileged program,
I'll still get the shell.
In this case, it did not reset its environment.
So the shell happens to be root owned.
Now, there are a couple of other things about
dynamic loading from the security point of view,
that are important to understand, and again,
these involve environment variables.
The first one, is that the dynamic loading may cause
your program to execute
other programs as part
of the library functions you're loading,
and you may not realize this.
As a very good example,
on some programs, ld, for example,
would clean out its environment variables,
so that IFS would be the standard.
But arch, which was called from
the same function and returns
the architecture of the processor,
didn't reset that, didn't check that.
As a result, if I use IFS,
I would be able to succeed with arch,
even though I wouldn't have been able to succeed with ld.
Again, the environment needs to be reset
to a trusted state whenever
you're doing anything privileged.
Dynamic loading basically says,
"No, it's not trusted.
You're going to take whatever you get.
You want to take the appropriate library,
the first library in the path."
FreeBSD fix this by the way in a very interesting way.
You can't give a relative path name
to a library and have it loaded.
If you're going to do dynamic loading,
you have to name the library with the complete path name.
So that way, you can name
the libraries in the system area.
Even if I try the attacks earlier,
it won't work because you have to get hold of
a library that's under my control.
If things are set up,
so the system will only go to system libraries,
then I'll never have control.
So, this is the end of
the dynamic loading part
of the environment variable lesson.
So, let's go on to the next one,
where we're going to talk about implicit as
opposed to explicit use of these environment variables.