require 'spec_helper' require 'ronin/exploits/exploit' describe Ronin::Exploits::Exploit do it "must include Ronin::Core::Metadata::ID" do expect(described_class).to include(Ronin::Core::Metadata::ID) end it "must include Ronin::Core::Metadata::Authors" do expect(described_class).to include(Ronin::Core::Metadata::Authors) end it "must include Ronin::Core::Metadata::Summary" do expect(described_class).to include(Ronin::Core::Metadata::Summary) end it "must include Ronin::Core::Metadata::Description" do expect(described_class).to include(Ronin::Core::Metadata::Description) end it "must include Ronin::Core::Metadata::References" do expect(described_class).to include(Ronin::Core::Metadata::References) end it "must include Ronin::Core::Params::Mixin" do expect(described_class).to include(Ronin::Core::Params::Mixin) end it "must include Ronin::Support::CLI::Printing" do expect(described_class).to include(Ronin::Support::CLI::Printing) end describe ".register" do context "when .register is not called in the Exploit class" do module TestExploits class UnregisteredExploit < Ronin::Exploits::Exploit end end subject { TestExploits::UnregisteredExploit } it "must not set .id" do expect(subject.id).to be(nil) end end context "when .register is called in the Exploit class" do module TestExploits class RegisteredExploit < Ronin::Exploits::Exploit register 'registered_exploit' end end subject { TestExploits::RegisteredExploit } it "must set .id" do expect(subject.id).to eq('registered_exploit') end it "must add the exploit class to Exploits.registry" do expect(Ronin::Exploits.registry['registered_exploit']).to be(subject) end end end describe ".quality" do context "when the quality is not set in the Exploit class" do module TestExploits class ExploitWithoutQuality < Ronin::Exploits::Exploit end end subject { TestExploits::ExploitWithoutQuality } it "must default to nil" do expect(subject.quality).to be(nil) end end context "when the quality is set in the Exploit class" do module TestExploits class ExploitWithQuality < Ronin::Exploits::Exploit quality :high end end subject { TestExploits::ExploitWithQuality } it "must return the set quality" do expect(subject.quality).to be(:high) end end end describe ".release_date" do context "when the release_date is not set in the Exploit class" do module TestExploits class ExploitWithoutReleaseDate < Ronin::Exploits::Exploit end end subject { TestExploits::ExploitWithoutReleaseDate } it "must default to nil" do expect(subject.release_date).to be(nil) end end context "when the release_date is set in the Exploit class" do module TestExploits class ExploitWithReleaseDate < Ronin::Exploits::Exploit release_date '2022-01-02' end end subject { TestExploits::ExploitWithReleaseDate } it "must return the set release_date as a Date object" do expect(subject.release_date).to be_kind_of(Date) expect(subject.release_date.year).to eq(2022) expect(subject.release_date.month).to eq(1) expect(subject.release_date.day).to eq(2) end end end describe ".released?" do context "when the release_date is not set in the Exploit class" do module TestExploits class ExploitWithoutReleaseDate < Ronin::Exploits::Exploit end end subject { TestExploits::ExploitWithoutReleaseDate } it "must return false" do expect(subject.released?).to be(false) end end context "when the release_date is set in the Exploit class" do module TestExploits class ExploitWithReleaseDate < Ronin::Exploits::Exploit release_date '2022-01-02' end end subject { TestExploits::ExploitWithReleaseDate } it "must return true" do expect(subject.released?).to be(true) end end end describe ".disclosure_date" do context "when the disclosure_date is not set in the Exploit class" do module TestExploits class ExploitWithoutDisclosureDate < Ronin::Exploits::Exploit end end subject { TestExploits::ExploitWithoutDisclosureDate } it "must default to nil" do expect(subject.disclosure_date).to be(nil) end end context "when the disclosure_date is set in the Exploit class" do module TestExploits class ExploitWithDisclosureDate < Ronin::Exploits::Exploit disclosure_date '2022-01-02' end end subject { TestExploits::ExploitWithDisclosureDate } it "must return the set disclosure_date as a Date object" do expect(subject.disclosure_date).to be_kind_of(Date) expect(subject.disclosure_date.year).to eq(2022) expect(subject.disclosure_date.month).to eq(1) expect(subject.disclosure_date.day).to eq(2) end end end describe ".disclosed?" do context "when the disclosure_date is not set in the Exploit class" do module TestExploits class ExploitWithoutDisclosureDate < Ronin::Exploits::Exploit end end subject { TestExploits::ExploitWithoutDisclosureDate } it "must return false" do expect(subject.disclosed?).to be(false) end end context "when the disclosure_date is set in the Exploit class" do module TestExploits class ExploitWithDisclosureDate < Ronin::Exploits::Exploit disclosure_date '2022-01-02' end end subject { TestExploits::ExploitWithDisclosureDate } it "must return true" do expect(subject.disclosed?).to be(true) end end end describe ".advisries" do module TestExploits class ExploitWithEmptyAdvisories < Ronin::Exploits::Exploit end end subject { TestExploits::ExploitWithEmptyAdvisories } it "must default to an empty Set" do expect(subject.advisories).to eq(Set[]) end end describe ".advisory" do module TestExploits class ExploitWithAdvisories < Ronin::Exploits::Exploit advisory 'CVE-2022-1234' end end subject { TestExploits::ExploitWithAdvisories } it "must add an Ronin::Exploits::Advisory object to .advisories" do expect(subject.advisories).to_not be_empty expect(subject.advisories).to all(be_kind_of(Ronin::Exploits::Advisory)) end context "when no URL is given" do context "and the advisory ID begins with 'CVE-' or 'GHSA-'" do let(:advisory) { subject.advisories.first } it "must generate the URL based on the advisory ID" do expect(advisory.url).to eq("https://nvd.nist.gov/vuln/detail/CVE-2022-1234") end end context "but the advisory ID does is not from a recognized vendor" do module TestExploits class ExploitWithUnrecognizedAdvisoryID < Ronin::Exploits::Exploit advisory 'FOO-12345' end end subject { TestExploits::ExploitWithUnrecognizedAdvisoryID } let(:advisory) { subject.advisories.first } it "must default the advisory URL to nil" do expect(advisory.url).to be(nil) end end end context "when a URL is given with the advisory ID" do module TestExploits class ExploitWithAdvisoryIDAndURL < Ronin::Exploits::Exploit advisory 'FOO-12345', 'https://www.foosec.org/FOO-12345' end end subject { TestExploits::ExploitWithAdvisoryIDAndURL } let(:advisory) { subject.advisories.first } it "must set the URL of the advisory" do expect(advisory.url).to eq("https://www.foosec.org/FOO-12345") end end end describe ".software" do module TestExploitSoftware class WithNoSoftwareSet < Ronin::Exploits::Exploit end class WithSoftwareSet < Ronin::Exploits::Exploit software 'TestWare' end class InheritsItsSoftware < WithSoftwareSet end class OverridesItsInheritedSoftware < WithSoftwareSet software 'TestWare Deluxe' end end subject { test_class } context "and when software is not set in the class" do let(:test_class) { TestExploitSoftware::WithNoSoftwareSet } it "must default to nil" do expect(subject.software).to be(nil) end end context "and when software is set in the class" do let(:test_class) { TestExploitSoftware::WithSoftwareSet } it "must return the set software" do expect(subject.software).to eq('TestWare') end end context "but when the software was set in the superclass" do let(:test_class) { TestExploitSoftware::InheritsItsSoftware } it "must return the software set in the superclass" do expect(subject.software).to eq('TestWare') end context "but the software is overridden in the sub-class" do let(:test_class) { TestExploitSoftware::OverridesItsInheritedSoftware } it "must return the software set in the sub-class" do expect(subject.software).to eq('TestWare Deluxe') end end end end describe ".software" do module TestExploitSoftwareVersions class WithNoSoftwareVersionsSet < Ronin::Exploits::Exploit end class WithSoftwareVersionsSet < Ronin::Exploits::Exploit software_versions %w[ 0.1.0 0.2.0 0.3.0 ] end class InheritsItsSoftwareVersions < WithSoftwareVersionsSet end class OverridesItsInheritedSoftwareVersions < WithSoftwareVersionsSet software_versions %w[ 1.0.0 1.0.1 1.0.2 ] end end subject { test_class } context "and when software versions are not set in the class" do let(:test_class) { TestExploitSoftwareVersions::WithNoSoftwareVersionsSet } it "must default to nil" do expect(subject.software_versions).to be(nil) end end context "and when software versions are set in the class" do let(:test_class) { TestExploitSoftwareVersions::WithSoftwareVersionsSet } it "must return the set software" do expect(subject.software_versions).to eq(['0.1.0', '0.2.0', '0.3.0']) end end context "but when the software versions was set in the superclass" do let(:test_class) { TestExploitSoftwareVersions::InheritsItsSoftwareVersions } it "must return the software versions set in the superclass" do expect(subject.software_versions).to eq(['0.1.0', '0.2.0', '0.3.0']) end context "but the software versions are overridden in the sub-class" do let(:test_class) { TestExploitSoftwareVersions::OverridesItsInheritedSoftwareVersions } it "must return the software versions set in the sub-class" do expect(subject.software_versions).to eq(['1.0.0', '1.0.1', '1.0.2']) end end end end describe ".exploit_type" do subject { described_class } it { expect(subject.exploit_type).to eq(:exploit) } end describe "#perform_validate" do it "must call #validate_params" do expect(subject).to receive(:validate_params) subject.perform_validate end it "must also call #validate" do expect(subject).to receive(:validate) subject.perform_validate end end describe "#perform_test" do subject { described_class.new } it "must call #test" do expect(subject).to receive(:test) subject.perform_test end end describe "#test" do it "must return a Test::Unknown value" do expect(subject.test).to be_kind_of(Ronin::Exploits::TestResult::Unknown) expect(subject.test.message).to eq("no vulnerability testing logic defined") end end describe "#build" do subject { described_class.new } it "must return nil by default" do expect(subject.build).to be(nil) end it "must not set @payload by default" do subject.build expect(subject.instance_variable_get('@payload')).to be(nil) end end describe "#perform_build" do subject { described_class.new } it "must call #build" do expect(subject).to receive(:build) subject.perform_build end end describe "#launch" do subject { described_class.new } it "must return nil by default" do expect(subject.launch).to be(nil) end end describe "#perform_launch" do subject { described_class.new } it "must call #launch" do expect(subject).to receive(:launch) subject.perform_launch end end describe "#cleanup" do subject { described_class.new } it "must return nil by default" do expect(subject.cleanup).to be(nil) end end describe "#perform_cleanup" do subject { described_class.new } it "must call #cleanup" do expect(subject).to receive(:cleanup) subject.perform_cleanup end end describe "#exploit" do it "must call #build and #launch" do expect(subject).to receive(:build) expect(subject).to receive(:launch) subject.exploit end it "must return self" do expect(subject.exploit).to be(subject) end context "when given a block" do it "must call #build, #prelaunch, #launch, yield, then and #cleanup" do expect(subject).to receive(:build) expect(subject).to receive(:launch) expect(subject).to receive(:cleanup) expect { |b| subject.exploit(&b) }.to yield_with_args(subject) end end context "when given the dry_run: true keyword argument" do it "should not deploy during a dry-run of the exploit" do expect(subject).to receive(:build) expect(subject).to_not receive(:launch) expect(subject).to_not receive(:cleanup) subject.exploit(dry_run: true) end end end describe "#fail" do let(:message) { "exploit failed" } it "must raise an ExploitFailed exception with the given message" do expect { subject.fail(message) }.to raise_error(Ronin::Exploits::ExploitFailed,message) end end end