Class 29 (last one!) CS 372H 05 May 2011 On the board ------------ 1. Last time / admin notes 2. Unix security, continued --review security model --consequences and attacks 3. Mandatory access control --consequences and attacks NOTE: Final exam, 7:00--10:00 PM, UTC 1.130 --------------------------------------------------------------------------- 1. Last time Admin notes --final exam will attempt to be comprehensive: readings, lectures, coding, design, labs. --topics I guarantee to test: --coding with monitors and CVs. --you will write code, so make sure you are comfortable with these objects. don't just memorize the rules; internalize them by working examples. --Keith Winstein's lecture --Jon Howell's lecture --review Saturday night, 6:00 PM --office hours the day of the final 2. Unix A. Security model (i) process has a user ID and one or more group IDs [saw last time] (ii) access control on files, per UID and per GID [saw last time] (iii) special user: root (UID=0) to which access control doesn't apply --uid 0 all permissions --how do uid's get set? setuid() call uid=0 can change to any other uid other uid's cannot invoke setuid(), to a first approximation --Unix login runs as root checks username, password against /etc/shadow calls setuid(user), runs user's shell --Here's more detail on login --Unix users typically stored in files in /etc --key files include "passwd", "group", and, often, "shadow" or "master.passwd" --purpose of shadow file is to separate the material that needs to be user-visible (list of users and UIDs on the system) from the cryptographic material. passwords are never stored in the clear, but you don't want to expose "shadow" to users. they could then easily conduct an offline dictionary attack. making the shadow file non-world-readable addresses that. --for each user, the files contain: --the textual username (for example, "mwalfish" or "root") --numeric user ID and group IDs --one-way hash of user's password: (salt, H(salt,pasword)) [salt makes it harder to break many passwords at once: attacker who is working on the password file offline cannot just compare the hashes to pre-computed values; the attacker must, for each entry, pump every password through the hash function.] --other information, including user's full name, login shell, etc. --/usr/bin/login runs as root --Reads username and password from terminal --Looks up username in /etc/passwd, etc. --Computes H(salt, typed password) and checks that it matches the hash in the "shadow" or "password" file --If matches, sets group ID and user ID corresponding to username --Execute user's shell with execve system call --Unfortunately, this security model (where uid 0 can do anything) leads to lots of privileged code, which in turn means that bugs are particularly dangerous. --Here's an example that uses login. Consider that on some systems, rlogind (remote login) runs as root. --The "login" command takes a flag, "-f" which means "don't ask for password". If the flag is supplied, the login succeeds only if the requested username is the current user, or if the current user is root. Unfortunately, rlogind runs "login username". Attack on a particular buggy system: at the login screen, pass user "-froot". So it looks like this: login: -froot [no password] # Why this worked on that buggy system: login sees "-f" flag and asks, "Is the requesting user the same?" (Answer: yes. Both login and rlogind are running as root here.) (iv) there are certain operations that only root can do Examples: --binding to ports less than 1024 --change current process's user or group ID --mount or unmount file systems --opening raw sockets (so you can do something like ping remote machines, for example) --set clock --halt or reboot machine --change UIDs (so login program needs to run as root) [Problem: you need to have all of root's permission to do *any* of these things (yes, can drop privileges, but we'll see that's easier said than done). That is a *lot* of privilege to do any one action. That is problematic for reasons we'll see.] (v) some implicit privileges --file descriptors, etc. --fork() gives parent ability to control child, etc. --can ptrace() processes at same UID (sort of) result: everything is a bit incoherent B. setuid --Some legitimate actions require more privs than UID --E.g., how should users change their passwords? --Passwords are stored in root-owned /etc/passwd and /etc/shadow files (see above) --going to go into a bit of detail. why? because setuid/setgid() are the sole means on Unix to *raise* a process's privilege level --Solution: Setuid/setgid programs --special "setuid" bit in the permissions of a file --Run with privileges of file's owner or group --Each process has _real_ and _effective_ UID/GID -- _real_ is user who launched setuid program -- _effective_ is owner/group of file, used in access checks --for setuid programs, on exec() of binary, kernel sets effective uid = file uid --examples: [mig ~]$ ls -l `which ping` -rwsr-xr-x 1 root root 35680 2010-03-12 05:41 /bin/ping* [my-local-computer ~]$ ls -l `which passwd` -r-sr-xr-x 1 root wheel 70352 Jun 18 2009 /usr/bin/passwd* [note the 's'] --Obviously need to own file to set the setuid bit --Need to own file and be in group to set setgid bit --(idea: a way for root -- or another user -- to delegate its ability to do something.) --Examples: --/usr/bin/passwd : change a user's passwd. User needs to be able to run this, but only root can modify the password file. --/bin/su: acquire new user ID with correct password. --/sbin/ping: users need to be able to ping but only root is allowed to open "raw sockets" (example of incoherent security model; but very hard to fix) --Have to be EXTREMELY careful when writing setuid code [Imagine that you are writing or installing a setuid program. In other words, you are willing to let that program run with *your* privileges.] --Fundamental reason you need to be careful: very difficult to anticipate exactly how and in what environment the code will be run....yet when it runs, it runs with *your* privileges (where "your" equals "root" or "whoever set the setuid bit on some code they wrote") --NOTE: Attackers can run setuid programs any time (no need to wait for root to run a vulnerable job) --FURTHER NOTE: Attacker controls many aspects of program's environment --Example attacks: --change PATH or IFS (internal field separator). Now, if setuid program calls system(3), e.g., to run a Unix command, it runs the attacker's version of the command (if PATH was modified), perhaps with different arguments than it thinks (if IFS was modified). --fix: program has to set up its environment properly --fix: shell may simply refuse to run a setuid script --Close fd 2 before execing program --now, setuid program opens a file, for example the password file.... (normally, would be fd=3, but because fd 2 was closed, the file will be given fd 2). --then, the program later encounters an error message and does fprintf(stderr, "some error msg"). --result: the error message goes into the password file! --fix: for setuid programs, kernel will open dummy fds for 0,1,2 if not already open --Set maximum file size to zero (if, say, setuid program changes a password and then rebuilds some password database) --Other setuid things to think about --when can a process A send a signal to a process B? --certainly if they have the same effective UID --but we also need to be able to kill processes even if the launched process is running setuid (i.e., with effective UID=0) --so allow the signal if the real UIDs match, as well --can also send SIGCONT w/out UID match if in same session --when can a process ptrace another one? --ptrace() is a debugger system call --Lets one process modify another's memory --Setuid gives a program more privilege than invoking user --So don't let process ptrace more privileged process or another user's process e.g., Require sender to match real & effective UID of target --Also disable/ignore setuid if ptraced target calls exec() --Exception: root can ptrace anyone --the point of the above two examples is that these things are tricky to get right. --as another example, consider a setuid process that does a bunch of privileged things and then drops privileges to become user again --should be okay, right? *****--NO. once the process has seen something privileged and then become the user again, it can be ptraced(), and the confidential things it has seen (or the privileged resources that it holds) can be manipulated by an unprivileged user.**** --fix? make software much more complicated. separate a single process into separate ones, for example. D. Other attacks (a) another example of what happens when you acquire then release privileges --note: the "su" program is setuid root. "su user" becomes the user if the password is correct. --say that A and B unprivileged processes owned by attacker --say A ptraces B --say A exec()s "su user" to its own identity --with effective UID=0, su asks for password and waits --While su is superuser (effective UID = 0), B exec()s "su root" (B's exec() honors setuid since A's EUID = 0!; put differently, A is superuser, so no problem with B raising its privilege level via setuid.) --A types password, gets shell and is now attached to "su root". --can now manipulate "su root"'s memory to get root shell (b) TOCTTOU (time-of-check-to-time-of-use) --very common attack --say there's a setuid program that needs to log events to a file: fd = open(logfile, O_CREAT|O_WRONLY|O_TRUNC, 0666); --what's the problem? --setuid program shouldn't be able to write to file that user can't. thus: if (access(logfile, W_OK) < 0) return ERROR; fd = open(logfile, ....) should fix it, right? (access() checks real user ID, not effective user ID). NO! --here's the attack........ setuid program attacker creat("/tmp/X"); check access("/tmp/X") --> OK unlink("/tmp/X"); symlink("/tmp/X", "/etc/passwd") open("/tmp/X") --from the BSD man pages: "access() is a potential security hole and should never be used." --the issue is that access check and open are non-atomic --to fix this, have to jump through hoops: manually traverse paths. check at each point that the dir you're in is the one you expected to be in (i.e., that you didn't accidentally follow a symbolic link). maybe check that path hasn't been modified E. Thoughts / editorial --at a high level, the real issue is not ptrace(). it's not even buggy code. the real issue is that the correct version of the code is way harder to write than the incorrect version: --correct version has to traverse path manually --be super-careful when running as setuid --cannot just blame application writers; must also blame the interfaces with which they're presented. --rules are incoherent. not clear how permissions compaose --for all that, Unix security is actually quite inflexible: --can't pass privileges to other processes --can't have multiple privileges at once --not a very general mechanism (cannot create a user or group unless root) F. Other problems: --(used to be) easy to escape from chroot with uid=0 fd=open("/"), chroot("/tmp"), fchdir(fd), chroot("./../../../..") --can create hard link to any file, even if we can't read or write it --buggy code remains setuid even if root rm's it, installs fixed version --keep a copy of sensitive data for later when you can obtain root access 4. Other approaches to security A. Mandatory access control historically, military was interested in a different security model what if the enemy gains access to secret data? could email it somewhere. mandatory access control vs discretionary Unix is discretionary, because process can specify security policy --change permissions on files it writes to, give its privileges to others military goal was to disallow such discretion --mandatory policy set by security officer, processes have no choice typical model: Bell-LaPadula, concerned with secrecy system has subjects (e.g. processes) and objects (e.g. files) each subject and object is assigned a security level (c,s) c = classification (unclassified, secret, top-secret, ...) s = category set (nuclear, crypto, NATO, ...) "dominates" relation: (c1, s1) dominates (c2, s2) iff c2 < c1 and s2 a subset of s1 use the dominates relation for access control S does an operation on O if operation will allow S to observe O, check if L(S) dominates L(O) if operation will allow S to modify O, check if L(O) dominates L(S) the dominates relation is transitive, so we get nice properties malicious process cannot copy top-secret file to unclassified file hard to retrofit to Unix hard to use in practice (i) very specialized security level scheme, fits military but not many others (ii) hard to figure out when to declassify data -- everything becomes secret (iii) unclear interaction with traditional access control (Unix users, groups) --yet still need both mechanisms but SELinux is getting some interest and use, and it incorporates some of these ideas B. Covert channels --information leaks out. e.g., a secret process gets compromised. sure, it can't leak data by writing to a file. but it can consume CPU or not. --historically, very difficult to defend against this. C. Question: can you ensure privacy through encryption? --answer: DEPENDS ON THE THREAT MODEL (and what you mean by privacy). --what if you know that two nations are talking? --what if you know that Coca Cola is talking to bankruptcy lawyers? (don't need to know *what* information is being relayed. enough to know that two parties are communicating.) D. What about CAPTCHAs? (as an example of another security technique....) --can't work in every case: bot the human's computer, solve the CAPTCHA, ship somewhere else 5. In closing........ --You have learned about the x86, processes, virtual memory, concurrency and synchronization and deadlock, scheduling, I/O, disks, file systems, the log-structured file system, crash recovery, transactions, networks, a bit about distributed systems, copy protection (both the legal protections and the technical (non)protections), binary rewriting, software safety, and security --You have learned to write and debug low-level code --I think you now know a lot more than you did at the beginning. (For instance, I wager that if you were to go back now and do lab 2 again from scratch you would not find it terribly difficult.) --Congratulations on having learned all of these things! I hope you enjoyed it!