diff --git a/lib/ronin/exploits/mixins/version_detection.rb b/lib/ronin/exploits/mixins/version_detection.rb new file mode 100644 index 00000000..00159925 --- /dev/null +++ b/lib/ronin/exploits/mixins/version_detection.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true +# +# ronin-exploits - A Ruby library for ronin-rb that provides exploitation and +# payload crafting functionality. +# +# Copyright (c) 2007-2024 Hal Brodigan (postmodern.mod3 at gmail.com) +# +# ronin-exploits is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# ronin-exploits is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with ronin-exploits. If not, see . +# + +module Ronin + module Exploits + module Mixins + # + # Adds version detection methods. + # + # @api public + # + # @since 1.2.0 + # + module VersionDetection + # + # Attempts to determine the software version of the target system. + # + # @return [String, nil] + # The detected version string or `nil` if the version could not be + # detected. + # + # @abstract + # + # @api public + # + def detect_version + end + + # + # Determines if the target system is running a vulnerable version of + # software. + # + # @return [TestResult::Vulnerable, TestResult::NotVulnerable, TestResult::Unknown] + # Indicates whether the target system is running a vulnerable version + # of software. + # + # * {TestResult::Vulnerable} - the software version was detected and + # is vulnerable. + # * {TestResult::NotVulnerable} - the software version was detected + # but is not vulnerable. + # * {TestResult::Unknown} - the software version could not be + # detected. + # + def test + if (version = detect_version) + if vulnerable_version?(version) + Vulnerable("the target system is running a vulnerable version (#{version})") + else + NotVulnerable("the target system is not running a vulnerable version (#{version})") + end + else + Unknown("cannot determine the software version of the target system") + end + end + end + end + end +end diff --git a/spec/mixins/version_detection_spec.rb b/spec/mixins/version_detection_spec.rb new file mode 100644 index 00000000..e1e69744 --- /dev/null +++ b/spec/mixins/version_detection_spec.rb @@ -0,0 +1,71 @@ +require 'spec_helper' +require 'ronin/exploits/mixins/version_detection' + +require 'ronin/exploits/exploit' + +describe Ronin::Exploits::Mixins::VersionDetection do + module TestVersionDetectionMixin + class TestExploit < Ronin::Exploits::Exploit + + include Ronin::Exploits::Mixins::VersionDetection + + software_versions [ + '>= 1.2.3, < 2.0.0', + '>= 2.3.4, < 2.5.1' + ] + end + end + + let(:test_class) { TestVersionDetectionMixin::TestExploit } + + subject { test_class.new } + + describe "#detect_version" do + it "must return nil by default" do + expect(subject.detect_version).to be(nil) + end + end + + describe "#test" do + context "when #detect_version returns a String" do + context "and it's a vulnerable version" do + let(:detected_version) { '2.4.2' } + + before do + expect(subject).to receive(:detect_version).and_return(detected_version) + end + + it "must return Ronin::Exploits::TestResult::Vulnerable" do + result = subject.test + + expect(result).to be_kind_of(Ronin::Exploits::TestResult::Vulnerable) + expect(result.message).to eq("the target system is running a vulnerable version (#{detected_version})") + end + end + + context "but it's not a vulnerable version" do + let(:detected_version) { '3.0.0' } + + before do + expect(subject).to receive(:detect_version).and_return(detected_version) + end + + it "must return Ronin::Exploits::TestResult::NotVulnerable" do + result = subject.test + + expect(result).to be_kind_of(Ronin::Exploits::TestResult::NotVulnerable) + expect(result.message).to eq("the target system is not running a vulnerable version (#{detected_version})") + end + end + end + + context "when #detect_version returns nil" do + it "must return Ronin::Exploits::TestResult::Unknown" do + result = subject.test + + expect(result).to be_kind_of(Ronin::Exploits::TestResult::Unknown) + expect(result.message).to eq("cannot determine the software version of the target system") + end + end + end +end