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.