Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions lib/msf/core/exploit/local/persistence.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# -*- coding: binary -*-

module Msf
module Exploit::Local::Persistence
def initialize(info = {})
@persistence_service = Rex::Sync::Event.new(auto_reset=false)
@clean_up_rc = nil
super(
update_info(
info,
'DefaultOptions' => {},
# https://github.com/rapid7/metasploit-framework/pull/19676#discussion_r1907594308
'Stance' => Msf::Exploit::Stance::Passive,
'Passive' => true
)
)

register_advanced_options(
[
OptString.new('WritableDir', [true, 'A directory where we can write files', '']),
OptBool.new('CleanUpRc', [true, 'Create a cleanup resource file.', true])
]
)
end

def exploit
run_as_background = !datastore['DisablePayloadHandler']
print_warning('Payload handler is disabled, the persistence will be installed only.') unless run_as_background

# Call the install_persistence function
# must be declared inside the persistence module
install_persistence

save_cleanup_rc if datastore['CleanUpRc'] && !@clean_up_rc.empty?

@persistence_service.wait if run_as_background
end

def install_persistence
# to be overloaded by the module
end

def save_cleanup_rc
host = session.sys.config.sysinfo['Computer']
# Create Filename info to be appended to downloaded files
filenameinfo = '_' + ::Time.now.strftime('%Y%m%d.%M%S')
logs = ::File.join(Msf::Config.log_directory, 'persistence', Rex::FileUtils.clean_path(host + filenameinfo))
# Create the log directory
::FileUtils.mkdir_p(logs)

# logfile name
clean_rc = logs + ::File::Separator + Rex::FileUtils.clean_path(host + filenameinfo) + '.rc'
file_local_write(clean_rc, @clean_up_rc)

print_status("Meterpreter-compatible Cleaup RC file: #{clean_rc}")

report_note(host: host,
type: 'host.persistance.cleanup',
data: {
local_id: session.sid,
stype: session.type,
desc: session.info,
platform: session.platform,
via_payload: session.via_payload,
via_exploit: session.via_exploit,
created_at: Time.now.utc,
commands: @clean_up_rc
})
end

def cleanup
end
end
end
28 changes: 28 additions & 0 deletions lib/msf/core/exploit/local/timespec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# -*- coding: binary -*-

module Msf
module Exploit::Local::Timespec
TIMESPEC_REGEX = %r{
\b(
(?:[01]?\d|2[0-3]):[0-5]\d(?:\s?(?:AM|PM))? | # Matches HH:MM (12h/24h)
midnight | noon | teatime | now | # Matches special keywords
now\s?\+\s?\d+\s?(?:minutes?|hours?|days?|weeks?) | # Matches relative times
(?:mon|tue|wed|thu|fri|sat|sun)(?:day)? | # Matches named days
(?:next|last)\s(?:mon|tue|wed|thu|fri|sat|sun)(?:day)? | # Matches next/last weekday
\d{1,2}/\d{1,2}/\d{2,4} | # Matches MM/DD/YY(YY)
\d{1,2}\.\d{1,2}\.\d{2,4} | # Matches DD.MM.YY(YY)
\d{6} | \d{8} # Matches MMDDYY or MMDDYYYY
)\b
}xi # 'x' allows extended mode, 'i' makes it case-insensitive

#
# Attempts to validate a timespec.
#
# @param timespec [String] The timespec to test
# @return [Boolean] If the timespec is valid or not
#
def self.valid_timespec?(timespec)
!!(timespec =~ TIMESPEC_REGEX) # Ensures true/false return
end
end
end
22 changes: 22 additions & 0 deletions lib/msf/core/post/linux/user.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# -*- coding: binary -*-

module Msf
class Post
module Linux
module User
include ::Msf::Post::Common
#
# Returns a string of the user's home directory
#
def get_home_dir(user)
cmd_exec("grep '^#{user}:' /etc/passwd | cut -d ':' -f 6").chomp
# could also be: "getent passwd #{user} | cut -d: -f6"
end
# User
end
# Linux
end
# Post
end
# Msf
end
42 changes: 42 additions & 0 deletions spec/lib/msf/core/exploit/local/timespec_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
require 'spec_helper'

RSpec.describe Msf::Exploit::Local::Timespec do
describe '.valid_timespec?' do
it 'returns true for military time' do
expect(Msf::Exploit::Local::Timespec.valid_timespec?('14:30')).to eq(true)
end

it 'returns true for 12hr time' do
expect(Msf::Exploit::Local::Timespec.valid_timespec?('2:15 PM')).to eq(true)
end

it 'returns true for midnight' do
expect(Msf::Exploit::Local::Timespec.valid_timespec?('midnight')).to eq(true)
end

it 'returns true for now' do
expect(Msf::Exploit::Local::Timespec.valid_timespec?('now')).to eq(true)
end

it 'returns true for now plus time' do
expect(Msf::Exploit::Local::Timespec.valid_timespec?('now + 10 minutes')).to eq(true)
end

it 'returns true for relative days' do
expect(Msf::Exploit::Local::Timespec.valid_timespec?('next Monday')).to eq(true)
expect(Msf::Exploit::Local::Timespec.valid_timespec?('last Friday')).to eq(true) # unlikely to ever be used for our context
end

it 'returns true for mm/dd/yy based date' do
expect(Msf::Exploit::Local::Timespec.valid_timespec?('07/04/23')).to eq(true)
end

it 'returns true for mmddyy based date' do
expect(Msf::Exploit::Local::Timespec.valid_timespec?('010124')).to eq(true)
end

it 'returns true for dd.mm.yyyy based date' do
expect(Msf::Exploit::Local::Timespec.valid_timespec?('31.12.2023')).to eq(true)
end
end
end
37 changes: 37 additions & 0 deletions spec/lib/msf/core/post/linux/user_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
require 'spec_helper'

RSpec.describe Msf::Post::Linux::User do
subject do
mod = ::Msf::Module.new
mod.extend described_class
mod
end

describe '#get_home_dir' do
let(:user) { 'testuser' }
let(:expected_command) { "grep '^#{user}:' /etc/passwd | cut -d ':' -f 6" }

context 'when the user exists' do
it 'returns the home directory path from /etc/passwd' do
expect(subject).to receive(:cmd_exec)
.with(expected_command)
.and_return("/home/testuser\n")

result = subject.get_home_dir(user)
expect(result).to eq('/home/testuser')
end
end

context 'when the user does not exist in /etc/passwd' do
it 'returns an empty string' do
expect(subject).to receive(:cmd_exec)
.with(expected_command)
.and_return("\n")

result = subject.get_home_dir(user)
expect(result).to eq('')
end
end

end
end