French version

sh vs bash

I’m writing this article because I’ve noticed, on two occasions, behavior I didn’t understand. The system call

execve("/bin/sh", NULL, NULL)

was launching a bash terminal instead of sh. Since I was in an exploitation phase with a privilege escalation, and since bash drops privileges by default, I ended up with a shell that no longer had the binary’s privileges.

So here is a short post that will serve as a reminder to myself more than anything else. The two resources cited at the end of the article will let you dig a little deeper.

sh is a programming language described by the POSIX standard. It has several implementations such as dash, ksh, etc. So, in most operating systems, since sh is not an implementation, /bin/sh is a symbolic link to an implementation, often /bin/bash.

On my current operating system, I can see it

/bin/sh -> bash*

bash is therefore an implementation of sh, however many modules/extensions have been added to it. When we call /bin/bash, we get the sh implementation with a whole bunch of extensions.

However, the /bin/bash binary has implemented a check on argv[0] (which contains the command line that called the binary). If the binary was called via /bin/sh, then /bin/bash will try to mimic the behavior of historical versions of sh. Bash’s startup extensions will not be loaded, in particular the one that causes the loss of privilege elevation following the execution of a binary with the SUID bit set.

Here are a few examples

# Call via sh
$ /bin/sh
sh-3.4 $ 

# Call via bash
$ /bin/bash
bash-4.3 $ 

# System call execve("/bin/sh", NULL, NULL)
bash-4.3 $ 

# System call execve("/bin/sh", ["/bin/sh"], NULL)
sh-3.4 $ 

You can clearly see from these various examples that when the bash binary is executed, if argv[0] contains the path to sh then we get an imitation of the historical behavior of sh. Otherwise, it’s indeed the bash binary and all its extensions that are executed.

That’s why I was losing privileges during my exploitations. All I need to do is ensure that the second argument of execve points to an array containing the string "/bin/sh".

References