7

I was recently reading through a write up on a capture the flag Linux VulnHub machine. For privilege escalation, the author references 'bash function manipulation'. A Google search turns up very little on how this works. In short they run strings on a root SUID binary like so:

strings run on root SUID ELF binary

Then the author proceeded to create/export a bash function matching one of the programs called by the program (namely, "/usr/bin/sl"), like so;

enter image description here

My main question is, how does this work?

It's obvious to me the end result is getting the binary to run the bash function/code and thus the root shell, but it seems like magic. I wonder what the conditions to reproduce are. For instance, in this case;

  1. Could you use "/dev/tty"?
  2. What about "puts"?
  3. How about /usr/bin/aafire? Why or why not?
  4. Would it still work if the "nightmare" program just called "sl -al" without the FQ path?
  5. Does it matter what kind of binary/ELF the root SUID file is?

If this attack known by another name that would be helpful to know as well as Google is coming up short on this one.


EDIT:

I just ran across this related walk through, which breaks down and analyzes some of the code of the (root SUID) "nightmare" binary. However it leaves me with more questions, then answers, such as why does the "train()" method set the UID and GID to 0 (root) before calling "/usr/bin/sl -al", when the "nightmare" binary is already a root SUID?

train method nightmare binary

If it doesnt do this, does this mean that "/usr/bin/sl" would fail to run as root? If thats the case, then is it fair to say this is a contrived privilege escalation scenario that is unlikely in the real world?

n00b
  • 445
  • 2
  • 13

2 Answers2

3

What do setresuid() and setresgid() do?

In order to better understand this example, we need to have a look at these two functions and the three parameters they take.

From this answer (and the credentials(7) man page):

On Linux, each process has the following user and group identifiers:

  • Real user ID and real group ID. These IDs determine who owns the process. To summarize, this is who you are.

  • Effective user ID and effective group ID. These IDs are used by the kernel to determine the permissions that the process will have when accessing shared resources such as message queues, shared memory, and semaphores. On most UNIX systems, these IDs also determine the permissions when accessing files. However, Linux uses the file system IDs for this task. To summarize, this is what you can do.

  • Saved set-user-ID and saved set-group-ID. These IDs are used in set-user-ID and set-group-ID programs to save a copy of the corresponding effective IDs that were set when the program was executed. A set-user-ID program can assume and drop privileges by switching its effective user ID back and forth between the values in its real user ID and saved set-user-ID. To summarize, this is who you were before.

Further, it's important to understand the SetUID-Bit. What this does is it runs an executable program with the rights of the owner, not the one who calls it.

A security nightmare

Your question specifically mentions that nightmare is owned by root and has the SetUID-Bit set. What this means is that any user who is allowed to execute nightmare essentially executes it as root. This means that all child processes of nightmare - such as they are spawned when calling system() - are also executed as root.

But really now, what does setresuid() do?

In this context? Nothing. Since the user is already executing things as root, it doesn't affect the further exploitation in any way.

Could you use /dev/tty?

This question does not make much sense in this context. You have to look at the relevant code snippet to see where the string /dev/tty is coming from.

if (open("/dev/tty", O_RDWR) != -1) {
        fire();
        rax = sub_4008d0();
}

This means the program is trying to open /dev/tty for reading and writing and if that succeeds, calls fire().

By naming a bash function /dev/tty(), you do not get the desired results, as open() requires a path to a file as its first parameter. It's completely independent of bash and has no concept of bash functions.

What about puts()?

Similarly, this doesn't make much sense. strings isn't a magic and doesn't understand context. All it does is look for series of bytes that are printable ASCII characters.

puts() is just a C-function which outputs a string.

What about /usr/bin/aafire?

While I didn't personally try, it seems as if practically /usr/bin/aafire could have been used instead of /usr/bin/sl -al. If this is not correct, please comment and I will edit this part.

Would it still work if a relative path would be used?

Yes, actually even better! system() internally calls sh -c, which has to resolve the $PATH variable to find sl. If the path variable was modified to include . or any other user-writable directory with higher priority than /usr/bin, then I could just symlink ./sl to /bin/bash.

Does it matter what kind of binary/ELF the root SUID file is?

Yes, because the SetUID-Bit gets ignored on shell scripts. It must be a native executable.

1

The program nightmare has the stickybits set, which means any user that executed this command effectively has their UID set to 0, for purposes of this command.

The strings command shows the binary files that are compiled in the executable. One of which is /usr/bin/sl

Now, the trick is to get nightmare to run the sl program reliably every time (probably has something to do with the error).

The function command, in the current shell, is effectively an alias for the sl command. Thus, when nightmare tries to run /usr/bin/sl, the internal function will run in lieu of the sl command on disk.
E.g.:
alias /usr/bin/sl /bin/sh
Now, because nightmare is running with a UID of 0, and nightmare tries to run /usr/bin/ls (as root), which is aliases to /bin/sh, you end up with a Born Shell session running as root, even though the initial user does not have root access.

Scottie H
  • 244
  • 1
  • 9
  • thanks for taking the time to respond. unfortunately this does not answer the question (the exact mechanics, or the why, behind this exploit) beyond what i already stated in the question as my understanding of what is happening. – n00b Apr 09 '19 at 21:29