From be91e9d213a9d123c1123f137f5a7de9d289c7b7 Mon Sep 17 00:00:00 2001 From: dsainty Date: Mon, 17 Jun 2024 19:50:16 +1000 Subject: [PATCH] Working hash generation compatible with that which Gossamer Threads devleoped for gforums --- README.md | 1 + goss-gthash.rb | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 README.md create mode 100644 goss-gthash.rb diff --git a/README.md b/README.md new file mode 100644 index 0000000..045cd9b --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +The special sauce MD5 hashing "cracked" and in Ruby diff --git a/goss-gthash.rb b/goss-gthash.rb new file mode 100644 index 0000000..d88f4f3 --- /dev/null +++ b/goss-gthash.rb @@ -0,0 +1,96 @@ +require 'digest' + +# Constants +ITOA64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + +def to64(value, length) + result = "" + length.times do + result << ITOA64[value & 0x3f] + value >>= 6 + end + result +end + +def gossamer_md5_crypt(password, legacy_hash) + # Extract the salt from the legacy hash + parts = legacy_hash.split('$') + salt = parts[2] + + # Limit the salt to 8 characters + salt = salt[0, 8] + + magic = "$GT$" + + ctx = Digest::MD5.new + ctx.update(password) + ctx.update(magic) + ctx.update(salt) + + final = Digest::MD5.new + final.update(password) + final.update(salt) + final.update(password) + final_digest = final.digest + + password_length = password.length + + while password_length > 0 + ctx.update(final_digest[0, [password_length, 16].min]) + password_length -= 16 + end + + password_length = password.length + while password_length > 0 + if password_length & 1 != 0 + ctx.update("\x00") + else + ctx.update(password[0, 1]) + end + password_length >>= 1 + end + + final_digest = ctx.digest + + 1000.times do |i| + ctx1 = Digest::MD5.new + if i & 1 != 0 + ctx1.update(password) + else + ctx1.update(final_digest) + end + ctx1.update(salt) if i % 3 != 0 + ctx1.update(password) if i % 7 != 0 + if i & 1 != 0 + ctx1.update(final_digest) + else + ctx1.update(password) + end + final_digest = ctx1.digest + end + + result = '' + result << to64((final_digest[0].ord << 16) | (final_digest[6].ord << 8) | final_digest[12].ord, 4) + result << to64((final_digest[1].ord << 16) | (final_digest[7].ord << 8) | final_digest[13].ord, 4) + result << to64((final_digest[2].ord << 16) | (final_digest[8].ord << 8) | final_digest[14].ord, 4) + result << to64((final_digest[3].ord << 16) | (final_digest[9].ord << 8) | final_digest[15].ord, 4) + result << to64((final_digest[4].ord << 16) | (final_digest[10].ord << 8) | final_digest[5].ord, 4) + result << to64(final_digest[11].ord, 2) + + "#{magic}#{salt}$#{result}" +end + +def verify_gossamer_password(password, legacy_hash) + generated_hash = gossamer_md5_crypt(password, legacy_hash) + generated_hash == legacy_hash +end + +# Example usage +legacy_hash = "$GT$i5ZNZdfX$077FK6JU70HIr2pR1/uLP1" +password = "polyflex80" + +resulting_hash = gossamer_md5_crypt(password, legacy_hash) +puts "Generated hash: #{resulting_hash}" +puts "Legacy hash: #{legacy_hash}" +puts "Password verification: #{verify_gossamer_password(password, legacy_hash)}" +