[Penetration Testing] Linux Local Privilege Escalation: /dev/random K2

TRY HARDER: A Blog About Discovery

Author: Brennan Turner @BLTSEC
[Penetration Testing] Linux Local Privilege Escalation: /dev/random K2

Scenario: I have compromised a box by gaining shell access and my next goal is to escalate privileges. This article details the steps I took in order to successfully gain access to higher privileged accounts via some basic Linux privilege escalation techniques found in this article by @g0tmi1k as well as exploiting CVE-2004-1051 in order to gain root privileges.

Disclaimer: This is a test environment and the methods shown below shouldn’t be done in a production environment without proper authorization and documentation. Use these techniques responsibly and ethically and always test every flag, switch, command, and exploit in your own test environment prior to running in a customer’s environment.

Breakdown: I began to collect information about the current user by issuing the following command:

sudo -l

This command list the allowed and forbidden commands that can be run by the invoked user. I then ran the /bin/calc command and viewed the output. I then began to perform further analysis on the binary. Strace intercepts and records the system calls which are called by a process and the signals which are received by a process. The name of each system call, its arguments, and its return value are printed on standard error. I ran strace to specifically trace the “open” system calls which are called when opening files:

strace -e open /bin/calc > /dev/null

The .config directory nor libcalc.so exists on the machine at this point but can be created since the path is owned by the user account. I created the .config directory and then began to create the shared object:



static void x() __attribute__((constructor));

void x() {
    system(“cp /bin/bash /tmp/bash && chmod +s /tmp/bash && /tmp/bash -p”);

__attribute__((constructor)) is GCC specific syntax and runs when a shared library is loaded.

The system() function call copies bash to the tmp directory and modifies the suid bit of the binary to run as the user who owns the file. The copied bash binary is then executed in privileged mode. This means that the effective uid it is launched with is retained, whereas normally bash will set the effective uid to the actual uid (the user that invoked the process). Using the -p option allows the suid bit to be effective in allowing bash to retain the user that the suid bit is set to. In addition the -p option ignores and therefore doesn’t inherit some of the environment files and variables. At this point, I couldn’t launch a shell under the user2 account but I could execute /bin/calc under user2 as shown in the sudo -l output. I then created the shared object and proceeded with the privesc:

gcc -shared -o /home/user/.config/libcalc.so -fPIC /home/user/.config/libcalc.c
chmod +x /home/user/

Permissions were updated on the /home/user/ directory using chmod to give user2 the ability to use the shared object we created during the execution of /bin/calc. I started enumerating the user2 account by looking into the history file (in this case ~/.bash_history) and found an entry where user2 made an edit to the crontab. I then looked in the crontab and found the following file and looked at the content:

Looking at the two ruby dependencies I saw user2 had group permissions set on the zip file:

I then ran the following command to create another privileged bash shell. Once the crontab runs under the user3 account bash2 will be given permissions which we can take advantage of because of the suid bit set on bash2:

echo ‘`cp /bin/bash /tmp/bash2 && chmod +s /tmp/bash2`’ > /usr/local/share/gems/gems/rubyzip-1.2.1/lib/zip.rb

Executing bash2 yields us a shell with user3 set as the effective user (remember to run using the privileged mode option: -p):

Time to escalate further by creating a binary that will invoke an elevated shell by first setting the real uid to user3 using the setreuid and setregid functions and then making a system call that executes bash. The setreuid() function sets the real and effective user IDs of the current process to the values specified by the ruid and euid arguments. Similarly, the setregid() function does the same for the group. This can also be useful in the case of suid executables that drop privileges but have a saved user ID that still retains its former privileges:


int main(){
    setreuid(geteuid(), getuid());
    setregid(getegid(), getgid());
gcc /tmp/bash3.c -o bash3

Next, I looked for SUID files (files that are run as the owner, not the user who executed it):

find / -perm -u=s -type f -ls 2>/dev/null | grep ‘user3’

I then enumerated the file using strings to see if there was anything interesting I might find:

The interesting pieces being the setuid function, system function call, setgid function, and the /usr/bin/logname command.

The next command I ran was:

env -i SHELLOPTS=xtrace PS4=’$(cp /bin/bash /tmp/bash4 && chown root.root /tmp/bash4 && chmod +s /tmp/bash4)’ /bin/sh -c ‘/usr/local/bin/whoisme’

A great tool for debugging shell scripts is an execution trace. An execution trace lists each command before the shell runs it. You can enable execution trace for your script by simply adding set -x to the beginning of it. The default execution trace prompt is + (the plus sign) but can be changed by re-defining the PS4 shell variable. So I have given a brief description into the start of the command above but how did I get root privileges and what does the entire command above actually do? As detailed in CVE-2004-1051: The bash shell uses the value of the PS4 environment variable (after expansion) as a prefix for commands run in execution trace mode. Execution trace mode (xtrace) is normally set via bash’s -x command line option or interactively by running “set -o xtrace”. However, it may also be enabled by placing the string “xtrace” in the SHELLOPTS environment variable before bash is started. A malicious user with sudo access to a shell script that uses bash can use this feature to run arbitrary commands for each line of the script. I used this method when I defined the PS4 environment variable. This method created the modified bash binary (bash4) with the appropriate permissions set and allowed me to gain root privileges on the box.