{"id":32,"date":"2011-08-09T14:00:39","date_gmt":"2011-08-09T14:00:39","guid":{"rendered":"http:\/\/www.vidarholen.net\/contents\/blog\/?p=32"},"modified":"2011-07-28T10:39:26","modified_gmt":"2011-07-28T10:39:26","slug":"password-hashing-with-md5-crypt-in-relation-to-md5","status":"publish","type":"post","link":"https:\/\/www.vidarholen.net\/contents\/blog\/?p=32","title":{"rendered":"Password hashing with MD5-crypt in relation to MD5"},"content":{"rendered":"<p>If you haven&#8217;t reinstalled recently, chances are you&#8217;re using MD5-based passwords. However, the password hashes you find in \/etc\/shadow look nothing like what <code>md5sum<\/code> returns. <\/p>\n<p>Here&#8217;s an example:<\/p>\n<pre>\r\n\/etc\/shadow:\r\n$1$J7iYSKio$aEY4anysz.gtXxg7XlL6v1\r\n\r\nmd5sum:\r\n7c6483ddcd99eb112c060ecbe0543e86 \r\n<\/pre>\n<p>What&#8217;s the difference in generating these hashes? Why are they different at all?<\/p>\n<p>Just running md5sum on a password and storing that is just marginally more secure than storing the plaintext password. <\/p>\n<p>Thanks to GPGPUs, a modern gaming rig can easily try 5 billion such passwords per second, or go over the entire 8-character alphanumeric space in a day. With <a href=\"http:\/\/en.wikipedia.org\/wiki\/Rainbow_table\">rainbow tables<\/a>, a beautiful time&#8211;space tradeoff, you can do pretty much the same in 15 minutes.<\/p>\n<p>MD5-crypt employs <a href=\"http:\/\/en.wikipedia.org\/wiki\/Salt_%28cryptography%29\">salting<\/a> to make precomputational attacks exponentially more difficult. Additionally, it uses <a href=\"http:\/\/en.wikipedia.org\/wiki\/Key_stretching\">stretching<\/a> to make brute force attacks harder (but just linearly so). <\/p>\n<p>As an aside, these techniques were used in the original crypt from 1979, so there&#8217;s really no excuse to do naive password hashing anymore. However, at that time the salt was 12 bits and the number of rounds 25 &#8212; quite adorable in comparison with today&#8217;s absolute minimum of 64 bits and 1000 rounds.<\/p>\n<p>The original crypt was <a href=\"http:\/\/en.wikipedia.org\/wiki\/Data_Encryption_Standard\">DES<\/a> based, but used a modified algorithm to prevent people from using existing DES cracking hardware. MD5-crypt doesn&#8217;t do any such tricks, and can be implemented in terms of any MD5 library, or even the md5sum util. <\/p>\n<p>As regular reads might suspect, I&#8217;ve written a shell script to demonstrate this: <a href=\"\/contents\/junk\/files\/md5crypt.bash\">md5crypt<\/a>. There are a lot of workarounds for Bash&#8217;s inability to handle NUL bytes in strings. It takes 10 seconds to generate a hash, and is generally awful..ly funny! <\/p>\n<p>Let&#8217;s first disect a crypt hash. man 3 crypt has some details. <\/p>\n<pre>\r\nIf salt is a character string starting with the characters\r\n\"$id$\" followed by a string terminated by \"$\":\r\n\r\n       $id$salt$encrypted\r\n\r\nthen instead of using the DES machine, id  identifies  the\r\nencryption  method  used  and this then determines how the\r\nrest of the password string is interpreted.  The following\r\nvalues of id are supported:\r\n\r\n       ID  | Method\r\n       -------------------------------------------------\r\n       1   | MD5\r\n       2a  | Blowfish (on some Linux distributions)\r\n       5   | SHA-256 (since glibc 2.7)\r\n       6   | SHA-512 (since glibc 2.7)\r\n\r\n<\/pre>\n<p>Simple and easy. Split by $, and then your fields are Algorithm, Salt and Hash. <\/p>\n<p>md5-crypt is a function that takes a plaintext password and a salt, and generate such a hash.<\/p>\n<p>To set a password, you&#8217;d generate a random salt, input the user&#8217;s password, and write the hash to \/etc\/shadow. To check a password, you&#8217;d read the hash from \/etc\/shadow, extract the salt, run the algorithm on this salt and the candidate password, and then see if the resulting hash matches what you have.<\/p>\n<p>md5-crypt can be divided into three phases. Initialization, loop, and finalization. Here&#8217;s a very high level description of what we&#8217;ll go through in detail:<\/p>\n<ol>\n<li>Generate a simple md5 hash based on the salt and password<\/li>\n<li>Loop 1000 times, calculating a new md5 hash based on the previous hash concatenated with alternatingly the password and the salt.<\/li>\n<li>Use a special base64 encoding on the final hash to create the password hash string<\/li>\n<\/ol>\n<p>&nbsp;<\/p>\n<p>Put like this, it relatively elegant. However, there are a lot of details that turn this from elegant to eyerolling. <\/p>\n<p>Here&#8217;s the real initialization.<\/p>\n<ol>\n<li>Let &#8220;password&#8221; be the user&#8217;s ascii password, &#8220;salt&#8221; the ascii salt (truncated to 8 chars), and &#8220;magic&#8221; the string &#8220;$1$&#8221;<\/li>\n<li>Start by computing the Alternate sum, <code>md5(password + salt + password)<\/code><\/li>\n<li>Compute the Intermediate<sub>0<\/sub> sum by hashing the concatenation of the following strings:\n<ol>\n<li>Password<\/li>\n<li>Magic<\/li>\n<li>Salt<\/li>\n<li>length(password) bytes of the Alternate sum, repeated as necessary<\/li>\n<li>For each bit in length(password), from low to high and stopping after the most significant set bit\n<ul>\n<li>If the bit is set, append a NUL byte<\/li>\n<li>If it&#8217;s unset, append the first byte of the password<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<\/ol>\n<p>&nbsp;<\/p>\n<p>I know what you&#8217;re thinking, and yes, it&#8217;s very arbitrary. The latter part was most likely a bug in the original implementation, carried along as UNIX issues often are. Remember to stay tuned next week, when we&#8217;ll compare this to SHA512-crypt as used on new installations!<\/p>\n<p>From this point on, the calculations will only involve the password, salt, and Intermediate<sub>0<\/sub> sum. Now we loop 1000 times, to stretch the algorithm.<\/p>\n<ul>\n<li>For i = 0 to 999 (inclusive), compute Intermediate<sub>i+1<\/sub> by concatenating and hashing the following:\n<ol>\n<li>If i is even, Intermediate<sub>i<\/sub><\/li>\n<li>If i is odd, password<\/li>\n<li>If i is not divisible by 3, salt<\/li>\n<li>If i is not divisible by 7, password<\/li>\n<li>If i is even, password<\/li>\n<li>If i is odd, Intermediate<sub>i<\/sub><\/li>\n<\/ol>\n<p>At this point you don&#8217;t need Intermediate<sub>i<\/sub> anymore.\n<\/li>\n<\/ul>\n<p>You will now have ended up with Intermediate<sub>1000<\/sub>. Let&#8217;s call this the Final sum. Since MD5 is 128bit, this is 16 bytes long.<\/p>\n<p>The bytes will be rearranged, and then encoded as 22 ascii characters with a special base64-type encoding. This is not the same as regular base64:<\/p>\n<pre>\r\nNormal base64 set:\r\nABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+\/\r\n\r\nCrypt base64 set:\r\n.\/0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\r\n<\/pre>\n<p>Additionally, there is no padding. The leftover byte will be encoded into 2 base64 ascii characters.<\/p>\n<ol>\n<li>Output the magic<\/li>\n<li>Output the salt<\/li>\n<li>Output a &#8220;$&#8221; to separate the salt from the encrypted section<\/li>\n<li>Pick out the 16 bytes in this order: 11 4 10 5 3 9 15 2 8 14 1 7 13 0 6 12.\n<ul>\n<li>For each group of 6 bits (there are 22 groups), starting with the least significant\n<ul>\n<li>Output the corresponding base64 character with this index<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<p>Congratulations, you now have a compatible md5-crypt hash!<\/p>\n<p>As you can see, it&#8217;s quite far removed from a naive <code>md5(password)<\/code> attempt. <\/p>\n<p>Fortunately, one will only ever need this algorithm for compatibility. New applications can use the standard PBKDF2 algorithm, implemented by most cryptography libraries, which does the same thing only in a standardized and parameterized way.<\/p>\n<p>As if this wasn&#8217;t bad enough, the next post next week will be more of the same, but with SHA512-crypt!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you haven&#8217;t reinstalled recently, chances are you&#8217;re using MD5-based passwords. However, the password hashes you find in \/etc\/shadow look nothing like what md5sum returns. Here&#8217;s an example: \/etc\/shadow: $1$J7iYSKio$aEY4anysz.gtXxg7XlL6v1 md5sum: 7c6483ddcd99eb112c060ecbe0543e86 What&#8217;s the difference in generating these hashes? Why are they different at all? Just running md5sum on a password and storing that is &hellip; <a href=\"https:\/\/www.vidarholen.net\/contents\/blog\/?p=32\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Password hashing with MD5-crypt in relation to MD5&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true},"categories":[5,4,22],"tags":[33,34,35],"class_list":["post-32","post","type-post","status-publish","format-standard","hentry","category-advanced-linux","category-linux","category-security","tag-hashing","tag-md5","tag-password"],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/www.vidarholen.net\/contents\/blog\/index.php?rest_route=\/wp\/v2\/posts\/32","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.vidarholen.net\/contents\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.vidarholen.net\/contents\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.vidarholen.net\/contents\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.vidarholen.net\/contents\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=32"}],"version-history":[{"count":0,"href":"https:\/\/www.vidarholen.net\/contents\/blog\/index.php?rest_route=\/wp\/v2\/posts\/32\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.vidarholen.net\/contents\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=32"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.vidarholen.net\/contents\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=32"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.vidarholen.net\/contents\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=32"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}