Module: Abusing Linux SUID

This module explores the impact of SUID misuse on the security of a system.


The slides for this module are:


Challenge set 1

Practice challenges for this module let aspiring hackers practice the (mis)use of Linux software! Specifically, upon connecting, the hacker can choose a single binary on the system to be set SUID, and will then be provided a shell on a Linux environment. In this environment, there is a file /flag, containing the secret flag that you must read out. However, the file is only readable by root user! Using your single SUID binary, you must elevate your privileges enough to read out the flag.

A single SUID binary will net you a single flag. That is, specifying /bin/cat and using it to leak out the flag (as in the example below) will get you one point, and specifying /usr/bin/tail and using it to leak out the flag (as in another example below) will get you another. In a more complex case, specifying /bin/chmod (as in the example below) and then using that to change the permission of other programs and read out the flag that way will get you a flag for chmod regardless of what other permissions you change, since /bin/chmod was the specified command.

For this module, each flag will earn one point.

Challenge set 2

Challenge set 2 is the revenge of challenge set 1 1. In set 1, it was enough to fool a SUID program into reading the /flag file and printing it out to you. In set 2, 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 harder.

For this assignment, each flag will earn two points.

Example Interaction

Here is a sample interaction that successfully retrieves the flag by setting the SUID flag on /bin/cat (you may use this for one of your solutions!), thus allowing /bin/cat to run as root. cat is a program that concatenates files and prints them out to standard out (if this is confusing, you are behind. You need to read the resources linked below to get un-confused). Thus, retrieving the flag with it is quite simple:

myuser@mylaptop:~$ ssh's password: 
[+++] Hacker Alias: ducky007
[+++] ASURITE: 123
[+++] 1. Show Scoreboard
[+++] 2. Solve HW1
[+++] 3. Quit
[+++] Choice: 2
[+++] Path to Binary: /bin/cat
hw1@b38bdd753b5b:~$ cat /flag
hw1@b38bdd753b5b:~$ exit
[+++] Flag: CSE466{747985b99bd25b8805ced639297720ae71e87a7acef580dc6b514143e5152133}
[+++] Correct Flag!

As you can see, this gives you the flag associated with /bin/cat. Let’s choose another program: say, /usr/bin/tail (you may use this for another solution!). tail is a program that prints out the last few lines of a file. Since the /flag file only has one line (the flag), this is perfect for us! Specifying /usr/bin/tail, you will receive another flag:

[+++] 1. Show Scoreboard
[+++] 2. Solve HW1
[+++] 3. Quit
[+++] Choice: 2
[+++] Path to Binary: /usr/bin/tail
hw1@b38bdd753b5b:~$ tail /flag
hw1@b38bdd753b5b:~$ exit
[+++] Flag: CSE466{7ca9d3c2fcbaa0c0cde22777bfefff2a5f5ac707f5194f91cd24226dbae0b74b}
[+++] Correct Flag!

Note that this is a different flag from /bin/cat! Now, you have two points: one for cat and one for tail. Note that, while /bin/cat and /usr/bin/tail is easy, other programs are not so simple to read the flag with. No matter how convoluted retrieving the flag is with a given program, one unique SUID binary path will only ever yield one flag.

For a slightly more complex example, let’s look at /bin/chmod. chmod is a program that can change permissions of files. There are many ways to read the /flag file with chmod. We’ll cover a few here (feel free to use this for one of your solutions!).

First, we can simply change the permissions of the /flag file to allow us to read it:

[+++] Path to Binary: /bin/chmod
hw1@5d1f52fff4e8:~$ chmod 644 /flag
hw1@5d1f52fff4e8:~$ cat /flag
hw1@5d1f52fff4e8:~$ exit
[+++] Flag: CSE466{36ef1e24753a8e3119eeac953e44f47f48aa388f9a72e2cb2d54fc9a622c5ef8}
[+++] Correct Flag!

Second, we can make the /bin/cat binary SUID, so that it runs as root and lets us read the flag.

[+++] Path to Binary: /bin/chmod
hw1@5d1f52fff4e8:~$ chmod 4755 /bin/cat
hw1@5d1f52fff4e8:~$ cat /flag
hw1@5d1f52fff4e8:~$ exit
[+++] Flag: CSE466{36ef1e24753a8e3119eeac953e44f47f48aa388f9a72e2cb2d54fc9a622c5ef8}
[+++] Correct Flag!

And we can do the same with other binaries, such as /usr/bin/tail:

[+++] Path to Binary: /bin/chmod
hw1@5d1f52fff4e8:~$ chmod 4755 /usr/bin/tail
hw1@5d1f52fff4e8:~$ cat /flag
hw1@5d1f52fff4e8:~$ exit
[+++] Flag: CSE466{36ef1e24753a8e3119eeac953e44f47f48aa388f9a72e2cb2d54fc9a622c5ef8}
[+++] Correct Flag!

Note that all three ways of getting the flag after specifying /usr/bin/chmod get the same flag. This is because the flag depends on the path to the binary that you specify in the Path to Binary: prompt. chmod is great, and it’ll let you run any binary with SUID, but it’ll only get you one flag.

Now you have three freebies. Go get the rest!

Example Interaction: Challenge Set 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 set 1, you will need to to figure out other programs with which to leak other flags.

HINT: Reading program documentation

To get a flag using a given program, you need to understand how the program works. For cat and tail it’s easy. Can you get the flag using /bin/whiptail, a program that is used to create TUIs (Text User Interfaces)? Hint: yes, but you need to know how to use whiptail!

So, how do you learn? There are two main ways: the program help and the program manual. The program help is generally accessed by using the -h, --help, or --usage options (i.e., whiptail --usage). The program manual is generally accessible using the man or info commands (man whiptail or info whiptail).

Unfortunately, the man command is broken on the homework server because of the sheer amount of random stuff installed on it to give you more targets SUID binaries to leak the flag. Until it is fixed, you can determine which package a given binary belongs to, install it in a separate docker container, and read the documentation there. To do this, you will need to understand what to install.

For example:

hw1@b38bdd753b5b:~$ dpkg -S /usr/bin/find

# on your own VM or docker container

# first, install man
you@computer:~$ sudo apt-get install manpages man-db

# then, install the application
you@computer:~$ sudo apt-get install findutils

# then, look up the man page!
you@computer:~$ man find

You can also find manuals on google. For example, googling man whiptail will bring up the whiptail manual.

This is a hacking challenge. You will have to abuse these programs. They might not be originally intended to read out files, but you can often misuse their functionality to do so. Their documentation is your friend.

Challenge Set 2: 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 set 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!

HINT: Dealing with errors

In the course of trying to abuse programs into giving you the flag, they might fail in weird ways. They might also fail in weird ways because of the way the container is run. As a rule of thumb, you should google all the errors that you get. Sometimes, the solution is quite simple!

/bin/whiptail is a great example of this: it doesn’t work at all right out of the box, but the error that it gives you (TERM environment variable needs set.), and the first result on google shows you how to fix that. There are other good examples of this (such as /bin/nano).

HINT: Picking your targets

There are a lot of targets to pick from (over 7,000), since you can SUID any ELF binary. You can get your list of targets by doing (inside the container):

hw1@f098f2fbfb1a:~$ find / -type f -executable -exec file \{\} \; | grep ELF | grep -o .*:

(HINT: understanding how that command works will get you another flag.) To succeed in this class, you should already have a good idea of which programs to start with. In general, almost any application that moves data from a file to somewhere else (the screen, another file, etc) can be used to leak the flag. For example, in the context of the useful resources below, consider how you would use /bin/cp to leak the flag? It is doable.

Once you have leaked the flag with a given program, look for similar programs. For example, tail and head are very similar, and can both be used to leak the flag.

Other hints

Also keep in mind a few hints:

  1. You don’t necessarily have to read the entire flag, cleanly, in one swoop. Some programs might mangle it (but in a way that you can unmangle), and some programs might only be able to leak a small amount of it in a single execution.
  2. This is a hacking challenge. There may be some “metagaming” that you can do. Look into how all parts of Linux work; it might help!
  3. Think very carefully about how programs present information to you when they don’t think that information is something critical. Debug info when debug flags are enabled. Error messages containing data that isn’t sensitive when it’s data you have access to anyways, but could be sensitive when the program has access to data that you don’t have access to. These situations expose methods that you can abuse certain programs to get flags.
  4. Sometimes, lazy programmers call out to other utilities instead of writing the functionality themselves. What happens if those other utilities are SUID root? What happens if you can influence where those other utilities are launched from (i.e., check out the PATH variable). This relies on lazy programming of the utilities that you are attacking, so you might want to pick up other targets before going down this path.

Other useful resources

Some other useful resources: