Flo
Flo

Reputation: 121

Platform-independently generate Linux password in Modular Crypt Format (MFC) SHA512 for /etc/shadow in Ruby

For automating the creation of virtual machines, I generate the passwords for /etc/shadow from some Ruby code. This is done on the host. The host can be Linux or Windows.

On a linux host

"mypassword".crypt("$6$" + "123SaLt9")

returns

$6$123SaLt9$idgUFrrwVkpDMcoHj7SAYH0UwCY6LymbsR9yTDrelYgcZ2wstoynmLIY83qBF0/BIT4Od.rQL2g3n2jEG/VXp/

which is correct and works for /etc/shadow.

However, on some Windows 10 hosts (since about two weeks ago on the machines of two colleagues) the same line of code returns

$6MrNddDu3Hf6

which, obviously, cannot work. (similar behaviour was observed for MacOS here https://judepereira.com/blog/use-ruby-to-generate-your-shadow-password/)

I overlooked that this function is platform dependent (https://apidock.com/ruby/String/crypt). Besides the fact that it did work a couple of weeks ago, I assume that I cannot take for granted that it works reliably on Windows 10 hosts.

What I need is a solution that returns the proper password hash in MFC format on Linux and Windows 10 hosts. As I understand, the password part of the MFC is not just a base64 encoded result of generating a SHA512 but rather a much more complicated process (https://akkadia.org/drepper/SHA-crypt.txt, Using ruby to generate SHA512 crypt-style hashes formatted for /etc/shadow?). I want to avoid to essentially implement the algorithm myself in Ruby. I did a couple of hours of research but couldn't find anything useful so far.

Any ideas how to avoid this problem on Windows, i.e. how to generate these password hashes platform-independently?

Upvotes: 0

Views: 652

Answers (1)

anothermh
anothermh

Reputation: 10554

As you discovered, the crypt method is platform-dependent. The algorithms it has available depend on the host. macOS only supports DES. Modern Linux distros will likely support every modern cipher. Windows -- well that's a total crapshoot and depends on how you're running Ruby. (Are you using WSL? Running Ruby directly on Windows? Something else?)

It's best to leave crypt out of the picture and use a library to handle it for you. First, install unix-crypt:

gem install unix-crypt

Then use it to generate your hash:

require 'unix_crypt'

# This is the hash you generated on Linux with crypt for your example
expected_hash = '$6$123SaLt9$idgUFrrwVkpDMcoHj7SAYH0UwCY6LymbsR9yTDrelYgcZ2wstoynmLIY83qBF0/BIT4Od.rQL2g3n2jEG/VXp/'

# Generate the same hash using unix-crypt instead
hash = UnixCrypt::SHA512.build('mypassword', '123SaLt9')

# Verify they match (returns true)
expected_hash == hash

This works for me on macOS where your original example does not:

"mypassword".crypt("$6$" + "123SaLt9")
=> "$6MrNddDu3Hf6"

So it should probably work on Windows as well. If it doesn't then I recommend you look at how your Windows users are running Ruby and switch them over to Windows Subsystem for Linux and have them use the specific Linux distro that you are using.

Once they have Linux up and running they can install Ruby and from the perspective of the Ruby interpreter it will be running on Linux, not Windows, so its access to crypt algorithms should be the same as what you have, and you should be able to use crypt as you did in your original example.

Alternatively, you can use the unix-crypt library from my example as a more universally compatible method of generating the hashes regardless of distro.

Upvotes: 1

Related Questions