From ce2e23cceffd9cbfd3ec3cf6bf252a17ba0ea032 Mon Sep 17 00:00:00 2001 From: Nayeraneru Date: Fri, 20 Feb 2026 22:28:05 +0200 Subject: [PATCH] add OptTimedelta datastore option and remove Kerberos-specific clock skew parsing --- .../core/exploit/remote/kerberos/client.rb | 7 +- .../kerberos/service_authenticator/options.rb | 7 +- lib/msf/core/opt_timedelta.rb | 64 +++++++++++++++++++ lib/rex/proto/mssql/client.rb | 4 +- .../exploit/remote/kerberos/client_spec.rb | 7 ++ spec/lib/msf/core/opt_timedelta_spec.rb | 30 +++++++++ 6 files changed, 109 insertions(+), 10 deletions(-) create mode 100644 lib/msf/core/opt_timedelta.rb create mode 100644 spec/lib/msf/core/opt_timedelta_spec.rb diff --git a/lib/msf/core/exploit/remote/kerberos/client.rb b/lib/msf/core/exploit/remote/kerberos/client.rb index da33c67771..41e7d55b0f 100644 --- a/lib/msf/core/exploit/remote/kerberos/client.rb +++ b/lib/msf/core/exploit/remote/kerberos/client.rb @@ -1,6 +1,6 @@ # -*- coding: binary -*- -require 'msf/core/exploit/remote/kerberos/clock_skew' +require 'msf/core/opt_timedelta' module Msf class Exploit @@ -45,8 +45,7 @@ module Msf register_advanced_options( [ - OptString.new('KrbClockSkew', [true, 'Adjust Kerberos client clock by this offset (e.g. 90s, -5m, 1h)', '0s'], - regex: Msf::Exploit::Remote::Kerberos::ClockSkew::CLOCK_SKEW_REGEX) + OptTimedelta.new('KrbClockSkew', [true, 'Adjust Kerberos client clock by this offset (e.g. 90s, -5m, 1h)', '0s']) ], self.class ) end @@ -90,7 +89,7 @@ module Msf # # @param value [String, Numeric, nil] def kerberos_clock_skew=(value) - @kerberos_clock_skew = Msf::Exploit::Remote::Kerberos::ClockSkew.parse(value) + @kerberos_clock_skew = Msf::OptTimedelta.parse(value) end # Returns the current time adjusted for Kerberos clock skew in UTC. diff --git a/lib/msf/core/exploit/remote/kerberos/service_authenticator/options.rb b/lib/msf/core/exploit/remote/kerberos/service_authenticator/options.rb index 7761f16283..1d539e6de9 100644 --- a/lib/msf/core/exploit/remote/kerberos/service_authenticator/options.rb +++ b/lib/msf/core/exploit/remote/kerberos/service_authenticator/options.rb @@ -3,7 +3,7 @@ # # This class stores Metasploit option configuration used across service authentication # -require 'msf/core/exploit/remote/kerberos/clock_skew' +require 'msf/core/opt_timedelta' module Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::Options # Create the list of options that a module must provide for Kerberos authentication via the given protocol @@ -38,10 +38,9 @@ module Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::Options fallbacks: ['Rhostname'], conditions: option_conditions ), - Msf::OptString.new( + Msf::OptTimedelta.new( 'KrbClockSkew', [true, 'Adjust Kerberos client clock by this offset (e.g. 90s, -5m, 1h)', '0s'], - regex: Msf::Exploit::Remote::Kerberos::ClockSkew::CLOCK_SKEW_REGEX, conditions: option_conditions ), Msf::OptAddress.new( @@ -66,6 +65,6 @@ module Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::Options # # @return [Float] def kerberos_clock_skew_seconds - Msf::Exploit::Remote::Kerberos::ClockSkew.parse(datastore['KrbClockSkew']) + Msf::OptTimedelta.parse(datastore['KrbClockSkew']) end end diff --git a/lib/msf/core/opt_timedelta.rb b/lib/msf/core/opt_timedelta.rb new file mode 100644 index 0000000000..c4ac01931a --- /dev/null +++ b/lib/msf/core/opt_timedelta.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +# -*- coding: binary -*- + +module Msf + class OptTimedelta < OptBase + TIMEDELTA_REGEX = /\A([+-]?\d+(?:\.\d+)?(?:[smhd])?)+\z/i.freeze + + UNIT_IN_SECONDS = { + 's' => 1, + 'm' => 60, + 'h' => 3_600, + 'd' => 86_400 + }.freeze + + attr_reader :allow_negative + + def initialize(in_name, attrs = [], allow_negative: true, **kwargs) + super(in_name, attrs, **kwargs) + @allow_negative = allow_negative + end + + def type + 'timedelta' + end + + def normalize(value) + self.class.parse(value) + end + + def valid?(value, check_empty: true, datastore: nil) + return false if check_empty && empty_required_value?(value) + + begin + parsed_value = self.class.parse(value) + rescue Msf::OptionValidateError + return false + end + + return false if !allow_negative && parsed_value.negative? + + super + end + + def self.parse(value) + return 0 if value.nil? + return value.to_f if value.is_a?(Numeric) + + trimmed_value = value.to_s.strip + return 0 if trimmed_value.empty? + return trimmed_value.to_f if trimmed_value.match?(/\A[+-]?\d+(?:\.\d+)?\z/) + raise Msf::OptionValidateError.new([], message: 'Invalid timedelta format') unless trimmed_value.match?(TIMEDELTA_REGEX) + + + total = 0 + trimmed_value.scan(/([+-]?\d+(?:\.\d+)?)([smhd]?)/i) do |amount, unit| + unit = 's' if unit.blank? + multiplier = UNIT_IN_SECONDS[unit.downcase] + total += amount.to_f * multiplier + end + total + end + end +end \ No newline at end of file diff --git a/lib/rex/proto/mssql/client.rb b/lib/rex/proto/mssql/client.rb index 7c3251ce82..c841fb71a8 100644 --- a/lib/rex/proto/mssql/client.rb +++ b/lib/rex/proto/mssql/client.rb @@ -3,7 +3,7 @@ require 'rex/proto/mssql/client_mixin' require 'rex/text' require 'msf/core/exploit' require 'msf/core/exploit/remote' -require 'msf/core/exploit/remote/kerberos/clock_skew' +require 'msf/core/opt_timedelta' module Rex module Proto @@ -389,7 +389,7 @@ module Rex framework: framework, framework_module: framework_module, ticket_storage: Msf::Exploit::Remote::Kerberos::Ticket::Storage::WriteOnly.new(framework: framework, framework_module: framework_module), - clock_skew: Msf::Exploit::Remote::Kerberos::ClockSkew.parse(framework_module.datastore['KrbClockSkew']) + clock_skew: Msf::OptTimedelta.parse(framework_module.datastore['KrbClockSkew']) ) kerberos_result = kerberos_authenticator.authenticate diff --git a/spec/lib/msf/core/exploit/remote/kerberos/client_spec.rb b/spec/lib/msf/core/exploit/remote/kerberos/client_spec.rb index 90e7890229..872c41aa51 100644 --- a/spec/lib/msf/core/exploit/remote/kerberos/client_spec.rb +++ b/spec/lib/msf/core/exploit/remote/kerberos/client_spec.rb @@ -11,6 +11,13 @@ RSpec.describe Msf::Exploit::Remote::Kerberos::Client do mod end + + describe 'KrbClockSkew option' do + it 'is registered as an OptTimedelta datastore option' do + expect(subject.options['KrbClockSkew']).to be_a(Msf::OptTimedelta) + end + end + describe '#kerberos_clock_skew' do it 'defaults to zero' do expect(subject.kerberos_clock_skew).to eq(0) diff --git a/spec/lib/msf/core/opt_timedelta_spec.rb b/spec/lib/msf/core/opt_timedelta_spec.rb new file mode 100644 index 0000000000..b43a5c9f0d --- /dev/null +++ b/spec/lib/msf/core/opt_timedelta_spec.rb @@ -0,0 +1,30 @@ +# -*- coding:binary -*- + +require 'spec_helper' + +RSpec.describe Msf::OptTimedelta do + valid_values = [ + { value: '120', normalized: 120.0 }, + { value: '-5m', normalized: -300.0 }, + { value: '1h30m', normalized: 5_400.0 }, + { value: '2d', normalized: 172_800.0 }, + { value: '+1.5h', normalized: 5_400.0 } + ] + + invalid_values = [ + { value: 'yolo' }, + { value: '1w' }, + { value: '5mfoo' } + ] + + it_behaves_like 'an option', valid_values, invalid_values, 'timedelta' + + describe '#valid?' do + it 'can enforce positive-only values' do + subject = described_class.new('Duration', [true, 'Duration'], allow_negative: false) + + expect(subject.valid?('5m')).to be(true) + expect(subject.valid?('-5m')).to be(false) + end + end +end \ No newline at end of file