Skip to content

Commit

Permalink
add checkin validation, rest of cmd_sessions tests
Browse files Browse the repository at this point in the history
  • Loading branch information
zgoldman-r7 committed Oct 11, 2023
1 parent 55f1451 commit 48f28f0
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 13 deletions.
45 changes: 40 additions & 5 deletions lib/msf/ui/console/command_dispatcher/core.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1842,7 +1842,15 @@ def get_matching_sessions(search_term)
return
end
end

unless checkin_searches.length() < 2
unless validate_checkin_searches(checkin_searches)
return
end
end

searches = []

unless id_searches.empty?
id_matches = {}
id_searches.each do |term|
Expand All @@ -1852,6 +1860,7 @@ def get_matching_sessions(search_term)
end
searches << id_matches
end

unless type_searches.empty?
type_matches = {}
type_searches.each do |term|
Expand All @@ -1861,6 +1870,7 @@ def get_matching_sessions(search_term)
end
searches << type_matches
end

unless checkin_searches.empty?
checkin_matches = {}
first_loop = true
Expand All @@ -1869,13 +1879,15 @@ def get_matching_sessions(search_term)
return unless matches
if first_loop
checkin_matches = matches
first_loop = false
else
checkin_matches = checkin_matches.select{ |session_id, session| matches[session_id] == session }
end
end
searches << checkin_matches
end
if searches

unless searches.empty?
matching_sessions = searches.first
searches[1..].each do |result_set|
matching_sessions = matching_sessions.select{| session_id, session| result_set[session_id] == session}
Expand All @@ -1887,12 +1899,38 @@ def get_matching_sessions(search_term)
matching_sessions
end

def validate_checkin_searches(checkin_searches)
if checkin_searches.length > 2
print_error("Too many checkin searches. Max: 2. Given: #{checkin_searches.length}")
end
valid_operators = ["before", "after"]
field1, operator1, value1 = checkin_searches[0].split(":")
field2, operator2, value2 = checkin_searches[1].split(":")
unless valid_operators.include?(operator1) && valid_operators.include?(operator2)
print_error("last_checkin can only be searched for using before or after. Ex: last_checkin:before:1m30s")
return false
end
if operator1 == operator2
print_error("Cannot search for last_checkin with two #{operator1} arguments.")
return false
end
if (operator1 == "before" && operator2 == "after" && parse_duration(value2) < parse_duration(value1)) || (operator1 == "after" && operator2 == "before" && parse_duration(value1) < parse_duration(value2))
print_error("After value must be a larger duration than the before value.")
return false
end
return true
end

def filter_sessions_by_search(search_term)
matching_sessions = {}
field = search_term.split(":")[0]
field, operator, value = search_term.split(":")
framework.sessions.each do |session_id, session|
case field
when "last_checkin"
unless operator == "before" || operator == "after"
print_error("Please specify before or after for checkin query. Ex: last_checkin:before:1m30s. Given: #{operator}")
return
end
if session.respond_to?(:last_checkin) && session.last_checkin && evaluate_search_criteria(session, search_term)
matching_sessions[session_id] = session
end
Expand Down Expand Up @@ -1920,9 +1958,6 @@ def evaluate_search_criteria(session, search_term)
return checkin_time < threshold_time
when "after"
return checkin_time > threshold_time
else
print_error("Please specify before or after for checkin query. Ex: last_checkin:before:1m30s. Given: #{operator}")
return false
end
when "session_id"
return session.sid.to_s == operator
Expand Down
99 changes: 91 additions & 8 deletions spec/lib/msf/ui/console/command_dispatcher/core_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,6 @@ def set_tabs_test(option)
end
end

#TODO: restructure hierarchy, test time parsing, and add more coverage
describe "#cmd_sessions" do
before(:each) do
allow(driver).to receive(:active_session=)
Expand Down Expand Up @@ -432,6 +431,7 @@ def set_tabs_test(option)
1 => instance_double(::Msf::Sessions::Meterpreter_x64_Win, last_checkin: Time.now, type: 'meterpreter', sid:1, sname: "sesh1", info: "info", session_host: "127.0.0.1", tunnel_to_s: "tunnel")
}
end

it "returns an error searching for an invalid parameter" do
core.cmd_sessions("--search", "not_a_term:1")
expect(@combined_output.join("\n")).to match_table <<~TABLE
Expand Down Expand Up @@ -533,21 +533,22 @@ def set_tabs_test(option)
context "searches with sessions with different checkin values" do
let(:sessions) do
{
1 => instance_double(::Msf::Sessions::Meterpreter_x64_Win, last_checkin: Time.now, type: 'meterpreter', sid:1, sname: "session1", info: "info", session_host: "127.0.0.1", tunnel_to_s: "tunnel"),
2 => instance_double(::Msf::Sessions::Meterpreter_x64_Win, last_checkin: (Time.now - 90), type: 'meterpreter', sid:2, sname: "session2", info: "info", session_host: "127.0.0.1", tunnel_to_s: "tunnel"),
3 => instance_double(::Msf::Sessions::Meterpreter_x64_Win, last_checkin: (Time.now - 2000), type: 'meterpreter', sid:3, sname: "session3", info: "info", session_host: "127.0.0.1", tunnel_to_s: "tunnel")
1 => instance_double(::Msf::Sessions::Meterpreter_x64_Win, last_checkin: Time.parse('Dec 18, 2022 12:33:40.000000000 GMT'), type: 'meterpreter', sid:1, sname: "session1", info: "info", session_host: "127.0.0.1", tunnel_to_s: "tunnel"),
2 => instance_double(::Msf::Sessions::Meterpreter_x64_Win, last_checkin: (Time.parse('Dec 18, 2022 12:33:40.000000000 GMT') - 90), type: 'meterpreter', sid:2, sname: "session2", info: "info", session_host: "127.0.0.1", tunnel_to_s: "tunnel"),
3 => instance_double(::Msf::Sessions::Meterpreter_x64_Win, last_checkin: (Time.parse('Dec 18, 2022 12:33:40.000000000 GMT') - 20000), type: 'meterpreter', sid:3, sname: "session3", info: "info", session_host: "127.0.0.1", tunnel_to_s: "tunnel")
}
end

it "filters by last checkin using fractions of a second" do
core.cmd_sessions("--search", "last_checkin:before:4.5s")
core.cmd_sessions("--search", "last_checkin:after:1m40.5s")
expect(@output.join("\n")).to match_table <<~TABLE
Active sessions
===============
Id Name Type Information Connection
-- ---- ---- ----------- ----------
1 session1 meterpreter info tunnel (127.0.0.1)
2 session2 meterpreter info tunnel (127.0.0.1)
3 session3 meterpreter info tunnel (127.0.0.1)
TABLE
end

Expand All @@ -563,13 +564,91 @@ def set_tabs_test(option)
3 session3 meterpreter info tunnel (127.0.0.1)
TABLE
end

it "warns the user when searching for an unknown checkin parameter" do
core.cmd_sessions("--search", "last_checkin:something:10s")
expect(@combined_output.join("\n")).to match_table <<~TABLE
Please specify before or after for checkin query. Ex: last_checkin:before:1m30s. Given: something
TABLE
end

it "filters by last checkin using before and after terms" do
core.cmd_sessions("--search", "last_checkin:after:200s last_checkin:before:30s")
expect(@output.join("\n")).to match_table <<~TABLE
Active sessions
===============
Id Name Type Information Connection
-- ---- ---- ----------- ----------
2 session2 meterpreter info tunnel (127.0.0.1)
TABLE
end

it "returns no matches if after and before are swapped" do
core.cmd_sessions("--search", "last_checkin:before:200s last_checkin:after:30s")
expect(@combined_output.join("\n")).to match_table <<~TABLE
After value must be a larger duration than the before value.
TABLE
end

it "returns a warning if middle argument is duplicated" do
core.cmd_sessions("--search", "last_checkin:before:200s last_checkin:before:30s")
expect(@combined_output.join("\n")).to match_table <<~TABLE
Cannot search for last_checkin with two before arguments.
TABLE
end
end

context "searches with sessions that have different checkins and types" do
let(:sessions) do
{
1 => instance_double(::Msf::Sessions::Meterpreter_x64_Win, last_checkin: Time.parse('Dec 18, 2022 12:33:40.000000000 GMT'), type: 'meterpreter', sid:1, sname: "session1", info: "info", session_host: "127.0.0.1", tunnel_to_s: "tunnel"),
2 => instance_double(::Msf::Sessions::Meterpreter_x64_Win, last_checkin: (Time.parse('Dec 18, 2022 12:33:40.000000000 GMT') - 90), type: 'java', sid:2, sname: "session2", info: "info", session_host: "127.0.0.1", tunnel_to_s: "tunnel"),
3 => instance_double(::Msf::Sessions::Meterpreter_x64_Win, last_checkin: (Time.parse('Dec 18, 2022 12:33:40.000000000 GMT') - 20000), type: 'cmd_shell', sid:3, sname: "session3", info: "info", session_host: "127.0.0.1", tunnel_to_s: "tunnel")
}
end

it "returns results when both fields match" do
core.cmd_sessions("--search", "last_checkin:after:1m40.5s session_type:meterpreter")
expect(@output.join("\n")).to match_table <<~TABLE
Active sessions
===============
Id Name Type Information Connection
-- ---- ---- ----------- ----------
1 session1 meterpreter info tunnel (127.0.0.1)
TABLE
end

it "does not return matches when only one field matches" do
core.cmd_sessions("--search", "last_checkin:after:1m40.5s session_type:something")
expect(@combined_output.join("\n")).to match_table <<~TABLE
No matching sessions.
TABLE
end
end

context "searches for checkin with sessions that do not respond to checkin" do
let(:sessions) do
{
1 => instance_double(::Msf::Sessions::Meterpreter_x64_Win, type: 'meterpreter', sid:1, sname: "session1", info: "info", session_host: "127.0.0.1", tunnel_to_s: "tunnel"),
}
end

it "returns no matches when searching against sessions without a checkin" do
core.cmd_sessions("--search", "last_checkin:after:6s")
expect(@combined_output.join("\n")).to match_table <<~TABLE
No matching sessions.
TABLE
end
end

context "with other flags" do
let(:sessions) do
{
1 => instance_double(::Msf::Sessions::Meterpreter_x64_Win, last_checkin: Time.now, type: 'meterpreter', sid:1, sname: "session1", info: "info", session_host: "127.0.0.1", tunnel_to_s: "tunnel"),
2 => instance_double(::Msf::Sessions::Meterpreter_x64_Win, last_checkin: (Time.now - 90), type: 'meterpreter', sid:2, sname: "session2", info: "info", session_host: "127.0.0.1", tunnel_to_s: "tunnel"),
3 => instance_double(::Msf::Sessions::Meterpreter_x64_Win, last_checkin: (Time.now - 2000), type: 'meterpreter', sid:3, sname: "session3", info: "info", session_host: "127.0.0.1", tunnel_to_s: "tunnel")
2 => instance_double(::Msf::Sessions::Meterpreter_x64_Win, last_checkin: Time.now, type: 'meterpreter', sid:2, sname: "session2", info: "info", session_host: "127.0.0.1", tunnel_to_s: "tunnel"),
3 => instance_double(::Msf::Sessions::Meterpreter_x64_Win, last_checkin: Time.now, type: 'meterpreter', sid:3, sname: "session3", info: "info", session_host: "127.0.0.1", tunnel_to_s: "tunnel")
}
end
it "Killall should not kill anything if nothing matches" do
Expand All @@ -595,4 +674,8 @@ def set_tabs_test(option)
end
end
end

describe "#parse_duration" do

end
end

0 comments on commit 48f28f0

Please sign in to comment.