Recover Root Account on Raspberry Pi and Alike

Recover Root Account on Raspberry Pi and Alike

Last Updated on February 24, 2025

Table of Contents

“Hey, Pal! How are you? Remember that little project I had on the CubieTruck? I resumed hacking on it. But it’s a pity, I lost my root password… I have to start all over again. Any chance you remember what silly password we choose together?”

If only it was the user’s account password, it would be easy: log as root, change the password on behalf of the user, done. That time, who is going to save the root account? Is there any rootroot account, by any chance? No. But don’t worry, we’re going to hack into the root account nonetheless. But how? It’s Linux, it’s super secure, it’s the root account, I choose a 50-bits-of-entropy-random-super-secure password… Maybe. But almost any security will fail, once the hacker has access to the hardware. And today, we’re the hacker!

“Hack” into the hardware

Let’s get access to the “hardware”: unplug the SD card! Then, plug it into your own system and mount the root partition of your Pi somewhere like /tmp/Rasp. Further in this document, I will consider only rescuing the root account on a flash card, but the exact same technique would work on any machine where you can get access to the root partition outside of its original system. This means for example unplugging the main hard drive and plugging it into another computer, or booting a live distribution on the hardware from which you can mount the root partition.

Some bits of theory. For those who don’t care, jump to next paragraph. On a Linux system, the /etc/passwd file stores the accounts information. Although that file also contained the passwords some years ago, the /etc/shadow file now takes care of them. The reason is that the information in the /etc/passwd file is necessary for a lot of common tools such as ls, so that it isn’t possible to hide this file and it is actually readable from anyone on the system. Of course, passwords are not stored in plain text, but still it weakens the security as it allows anyone on the system to retrieve the hash and run an offline attack, like brute forcing. That’s why passwords are today stored in another file, the /etc/shadow, which is only accessible to highly privileged processes, i.e. root. The old password field in the /etc/passwd is now just an “x”, telling the system to ask /etc/shadow instead.

Replace password

Now that the root partition is mounted on a foreign system, the original permissions system that would prevent someone other than root to access and modify the /etc/shadow doesn’t hold any more. Some strategies suggest simply emptying the password field, so that one could log into the account without passwords. But to my experience, this doesn’t work on systems with recent protection policies. Let’s not tickle the system too much, and try to find something more elegant.

First, there is a --root option to the passwd utility, which you can theoretically set to the mountpoint of the storage device

passwd -R /tmp/Rasp root

but it miserably failed in my case. Nevermind, let’s do exactly the same, but manually. With a plus point: we will learn something in the process.

In the /etc/shadow, the passwords are hashed and salted (yummy…). The method, the salt and the resulting hash are all stored in the form $method_id$salt$hash. We can’t decipher back the password, but now that we have access to the file, we can change this hash to be the one of a new password. Something that we will remember this time, if possible…

After some research of a nice little tool to do the job, I found out that the best was a simple python script. Just start a python console and type

$ python
Python 3.5.0 (default, Sep 20 2015, 11:28:25)
[GCC 5.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import crypt
>>> crypt.crypt('cilyan.org', crypt.mksalt(crypt.METHOD_SHA512))
'$6$2uhsNKMqZ/OoEfd6$7ihU49hjdfxb1O82fd5Kh2gJAvzWvUvFXS.yA/Wk6y.dO8cO/MBMWpJ4fyIol9BUYph.9seJ7wb2TqCjaaNHc.'
>>>

Above, 'cilyan.org' is our new password, and the resulting string is exactly what we have to place in the shadow file in the password field (just remember not to include the quotes). I used the SHA512 algorithm, which should also be standard to your system.

Updated on 24/02/2025

A quick and easy solution to get our salted hashed password, is using openssl:

openssl passwd -6

where -6 is for SHA512 algorithm. You can check the standard method for your system as follows:

grep ENCRYPT_METHOD /tmp/Rasp/etc/login.defs

But anyway, as the method used is stored in the string along the salt and hash, even if it isn’t the default one, you should be able to log in. Also, openssl cannot generate a yescrypt hash (yet?).

Replace the password in the shadow file, on the line for the root account, which should now look something like this

root:$6$2uhsNKMqZ/OoEfd6$7ihU49hjdfxb1O82fd5Kh2gJAvzWvUvFXS.yA/Wk6y.dO8cO/MBMWpJ4fyIol9BUYph.9seJ7wb2TqCjaaNHc.:15735::::::

And if you are really, really lazy, you may just copy the above line, paste it in your shadow file, and log onto your Raspberry Pi using the password “cilyan.org”!

Related Posts

Manjaro is a fantastic way to get a fully-featured Linux desktop up and running in minutes. Its graphical installer, sensible defaults, and curated package selection making it a great choice for both newcomers and power users. However, over time, I found that Manjaro’s repositories lagged behind Arch, and—at least for the XFCE flavor—stability was not what I expected. I wanted to keep my working setup, but move to a system that is easier and more stable to maintain: pure Arch Linux.

Read More

Last week, I was preparing a data analysis report using Jupyter, Pandas and Matplotlib (to only quote a few bricks of this wonderful framework). One of the figures had two subplots, the second being an enlargement of a region of the first. To make it obvious, and at the same time show the old MATLAB Fanclub how so 90 they were, I decided to put an arrow from the first to the second subplot.

Read More

SWIG is a wrapper generator that is able to connect compiled libraries to a bunch of scripting languages. The process is mostly automatic, but to tackle some corner cases, you have to help the generator do the right thing. In my library, all functions would return an integer, which is an error code. A special function, following the same behavior as strerror_r, can be used to retrieve the meaning of a special error code. This is a pretty usual mechanism for C code. But that’s not the way scripting languages work. In their world, functions are rather supposed to raise exceptions.

Read More