Skip to content

Commit f9c9764

Browse files
authored
Merge pull request #58 from P4X-ng/Q-DEV-issue-56-1765704413
Add malware (time bomb style)
2 parents 9946e46 + 29338c8 commit f9c9764

File tree

7 files changed

+1971
-1
lines changed

7 files changed

+1971
-1
lines changed

lib/msf/core/constants.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ module Msf
1919
MODULE_PAYLOAD = 'payload'
2020
MODULE_POST = 'post'
2121
MODULE_EVASION = 'evasion'
22+
MODULE_MALWARE = 'malware'
2223
MODULE_TYPES =
2324
[
2425
MODULE_ENCODER,
@@ -27,7 +28,8 @@ module Msf
2728
MODULE_NOP,
2829
MODULE_POST,
2930
MODULE_AUX,
30-
MODULE_EVASION
31+
MODULE_EVASION,
32+
MODULE_MALWARE
3133
]
3234

3335
#

lib/msf/core/malware.rb

Lines changed: 360 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,360 @@
1+
# -*- coding: binary -*-
2+
3+
#
4+
# A Malware simulation module with time bomb functionality
5+
#
6+
class Msf::Malware < Msf::Module
7+
8+
class Complete < RuntimeError
9+
end
10+
11+
class Failed < RuntimeError
12+
end
13+
14+
class TimeBombExpired < RuntimeError
15+
end
16+
17+
include Msf::PostMixin
18+
19+
# Track files and artifacts for cleanup
20+
attr_accessor :needs_cleanup
21+
attr_accessor :cleanup_artifacts
22+
attr_accessor :start_time
23+
attr_accessor :duration_seconds
24+
25+
def initialize(info = {})
26+
super
27+
28+
# Initialize cleanup tracking
29+
@cleanup_artifacts = []
30+
@needs_cleanup = false
31+
@start_time = nil
32+
@duration_seconds = nil
33+
34+
# Register common malware simulation options
35+
register_options([
36+
OptInt.new('DURATION', [true, 'Duration in seconds before auto-cleanup (time bomb)', 300]),
37+
OptBool.new('AUTO_CLEANUP', [true, 'Automatically cleanup artifacts when time expires', true]),
38+
OptBool.new('SIMULATE_ONLY', [true, 'Only simulate malware behavior without actual persistence', true]),
39+
OptString.new('CLEANUP_SIGNAL', [false, 'Custom signal file to trigger immediate cleanup', '']),
40+
])
41+
end
42+
43+
def setup
44+
m = replicant
45+
46+
if m.actions.length > 0 && !m.action
47+
raise Msf::MissingActionError, "Please use: #{m.actions.collect {|e| e.name} * ", "}"
48+
end
49+
50+
# Initialize time bomb
51+
@start_time = Time.now
52+
@duration_seconds = datastore['DURATION'].to_i
53+
@needs_cleanup = datastore['AUTO_CLEANUP']
54+
55+
print_status("Malware simulation started at #{@start_time}")
56+
print_status("Time bomb set for #{@duration_seconds} seconds")
57+
print_warning("Auto-cleanup #{@needs_cleanup ? 'ENABLED' : 'DISABLED'}")
58+
59+
# Msf::Module(Msf::PostMixin)#setup
60+
super
61+
end
62+
63+
def type
64+
Msf::MODULE_MALWARE
65+
end
66+
67+
def self.type
68+
Msf::MODULE_MALWARE
69+
end
70+
71+
#
72+
# Create an anonymous module not tied to a file. Only useful for IRB.
73+
#
74+
def self.create(session)
75+
mod = new
76+
mod.instance_variable_set(:@session, session)
77+
# Have to override inspect because for whatever reason, +type+ is coming
78+
# from the wrong scope and i can't figure out how to fix it.
79+
mod.instance_eval do
80+
def inspect
81+
"#<Msf::Malware anonymous>"
82+
end
83+
end
84+
mod.class.refname = "anonymous"
85+
86+
mod
87+
end
88+
89+
# This method returns the ID of the Mdm::Session that the malware module
90+
# is currently running against.
91+
#
92+
# @return [NilClass] if there is no database record for the session
93+
# @return [Integer] if there is a database record to get the id for
94+
def session_db_id
95+
if session.db_record
96+
session.db_record.id
97+
else
98+
nil
99+
end
100+
end
101+
102+
# Override Msf::Module#fail_with for Msf::Simple::Malware::job_run_proc
103+
def fail_with(reason, msg = nil)
104+
cleanup_artifacts if @needs_cleanup
105+
raise Msf::Malware::Failed, "#{reason.to_s}: #{msg}"
106+
end
107+
108+
#
109+
# Time bomb functionality
110+
#
111+
112+
# Check if the time bomb has expired
113+
def time_bomb_expired?
114+
return false unless @start_time && @duration_seconds
115+
(Time.now - @start_time) >= @duration_seconds
116+
end
117+
118+
# Get remaining time in seconds
119+
def time_remaining
120+
return 0 unless @start_time && @duration_seconds
121+
remaining = @duration_seconds - (Time.now - @start_time)
122+
[remaining, 0].max
123+
end
124+
125+
# Check for cleanup signal file
126+
def cleanup_signal_present?
127+
signal_file = datastore['CLEANUP_SIGNAL']
128+
return false if signal_file.blank?
129+
130+
begin
131+
if session.type == 'meterpreter'
132+
return session.fs.file.stat(signal_file) rescue false
133+
elsif session.type == 'shell'
134+
result = session.shell_command_token("test -f #{signal_file} && echo exists")
135+
return result.include?('exists')
136+
end
137+
rescue
138+
return false
139+
end
140+
false
141+
end
142+
143+
# Check if module should terminate (time bomb or signal)
144+
def should_terminate?
145+
time_bomb_expired? || cleanup_signal_present?
146+
end
147+
148+
# Add artifact for cleanup tracking
149+
def register_artifact(path, type = :file)
150+
@cleanup_artifacts << { path: path, type: type, created_at: Time.now }
151+
print_good("Registered artifact for cleanup: #{path}")
152+
end
153+
154+
# Cleanup all registered artifacts
155+
def cleanup_artifacts
156+
return unless @cleanup_artifacts && @cleanup_artifacts.any?
157+
158+
print_status("Time bomb expired! Cleaning up #{@cleanup_artifacts.length} artifacts...")
159+
160+
@cleanup_artifacts.each do |artifact|
161+
begin
162+
case artifact[:type]
163+
when :file
164+
cleanup_file(artifact[:path])
165+
when :directory
166+
cleanup_directory(artifact[:path])
167+
when :registry
168+
cleanup_registry(artifact[:path])
169+
when :service
170+
cleanup_service(artifact[:path])
171+
when :process
172+
cleanup_process(artifact[:path])
173+
else
174+
print_warning("Unknown artifact type: #{artifact[:type]}")
175+
end
176+
rescue => e
177+
print_error("Failed to cleanup #{artifact[:path]}: #{e.message}")
178+
end
179+
end
180+
181+
@cleanup_artifacts.clear
182+
print_good("Malware simulation cleanup completed")
183+
end
184+
185+
# Cleanup a file
186+
def cleanup_file(path)
187+
if session.type == 'meterpreter'
188+
session.fs.file.rm(path)
189+
elsif session.type == 'shell'
190+
if session.platform == 'windows'
191+
session.shell_command_token("del /f /q \"#{path}\"")
192+
else
193+
session.shell_command_token("rm -f \"#{path}\"")
194+
end
195+
end
196+
print_status("Removed file: #{path}")
197+
end
198+
199+
# Cleanup a directory
200+
def cleanup_directory(path)
201+
if session.type == 'meterpreter'
202+
session.fs.dir.rmdir(path)
203+
elsif session.type == 'shell'
204+
if session.platform == 'windows'
205+
session.shell_command_token("rmdir /s /q \"#{path}\"")
206+
else
207+
session.shell_command_token("rm -rf \"#{path}\"")
208+
end
209+
end
210+
print_status("Removed directory: #{path}")
211+
end
212+
213+
# Cleanup registry entry (Windows only)
214+
def cleanup_registry(path)
215+
return unless session.platform == 'windows'
216+
217+
if session.type == 'meterpreter'
218+
session.sys.registry.delete_key(path)
219+
elsif session.type == 'shell'
220+
session.shell_command_token("reg delete \"#{path}\" /f")
221+
end
222+
print_status("Removed registry key: #{path}")
223+
end
224+
225+
# Cleanup service (placeholder)
226+
def cleanup_service(name)
227+
print_status("Stopping and removing service: #{name}")
228+
# Implementation depends on platform and service type
229+
end
230+
231+
# Cleanup process (placeholder)
232+
def cleanup_process(pid_or_name)
233+
print_status("Terminating process: #{pid_or_name}")
234+
# Implementation depends on platform
235+
end
236+
237+
#
238+
# Safe simulation helpers
239+
#
240+
241+
# Simulate file creation without actual persistence
242+
def simulate_file_drop(path, content = nil)
243+
if datastore['SIMULATE_ONLY']
244+
print_status("SIMULATION: Would create file at #{path}")
245+
return true
246+
end
247+
248+
# Actually create the file but register for cleanup
249+
content ||= generate_fake_malware_content
250+
251+
if session.type == 'meterpreter'
252+
session.fs.file.upload_file(path, content)
253+
elsif session.type == 'shell'
254+
encoded_content = Rex::Text.encode_base64(content)
255+
if session.platform == 'windows'
256+
session.shell_command_token("echo #{encoded_content} | certutil -decode - \"#{path}\"")
257+
else
258+
session.shell_command_token("echo '#{encoded_content}' | base64 -d > \"#{path}\"")
259+
end
260+
end
261+
262+
register_artifact(path, :file)
263+
print_good("Created malware file: #{path}")
264+
true
265+
end
266+
267+
# Simulate registry persistence (Windows)
268+
def simulate_registry_persistence(key, value, data)
269+
if datastore['SIMULATE_ONLY']
270+
print_status("SIMULATION: Would create registry entry #{key}\\#{value}")
271+
return true
272+
end
273+
274+
return false unless session.platform == 'windows'
275+
276+
if session.type == 'meterpreter'
277+
session.sys.registry.set_value(key, value, data)
278+
elsif session.type == 'shell'
279+
session.shell_command_token("reg add \"#{key}\" /v \"#{value}\" /d \"#{data}\" /f")
280+
end
281+
282+
register_artifact("#{key}\\#{value}", :registry)
283+
print_good("Created registry persistence: #{key}\\#{value}")
284+
true
285+
end
286+
287+
# Generate fake malware content for simulation
288+
def generate_fake_malware_content
289+
content = "#!/bin/bash\n" if session.platform != 'windows'
290+
content ||= "@echo off\n"
291+
292+
content += <<~EOF
293+
REM Metasploit Malware Simulation
294+
REM This is a HARMLESS simulation for penetration testing
295+
REM Created: #{Time.now}
296+
REM Duration: #{@duration_seconds} seconds
297+
REM Auto-cleanup: #{@needs_cleanup}
298+
299+
echo "Malware simulation running..."
300+
echo "This is NOT real malware - it's a penetration testing simulation"
301+
echo "Time remaining: #{time_remaining} seconds"
302+
EOF
303+
304+
content
305+
end
306+
307+
# Main execution wrapper with time bomb checking
308+
def run_with_time_bomb
309+
begin
310+
# Start the malware simulation
311+
run_malware_simulation
312+
313+
# Monitor for time bomb expiration
314+
while !should_terminate?
315+
sleep(5) # Check every 5 seconds
316+
317+
if time_remaining <= 30 && time_remaining > 25
318+
print_warning("Time bomb warning: #{time_remaining} seconds remaining")
319+
end
320+
end
321+
322+
if time_bomb_expired?
323+
print_error("Time bomb expired!")
324+
raise Msf::Malware::TimeBombExpired, "Malware simulation time limit reached"
325+
elsif cleanup_signal_present?
326+
print_status("Cleanup signal detected")
327+
end
328+
329+
rescue Msf::Malware::TimeBombExpired => e
330+
print_error("Time bomb detonated: #{e.message}")
331+
rescue => e
332+
print_error("Malware simulation error: #{e.message}")
333+
ensure
334+
cleanup_artifacts if @needs_cleanup
335+
end
336+
end
337+
338+
# Override this method in specific malware modules
339+
def run_malware_simulation
340+
print_status("Base malware simulation - override this method in specific modules")
341+
342+
# Example simulation behavior
343+
simulate_file_drop("/tmp/malware_sim.txt") if session.platform != 'windows'
344+
simulate_file_drop("C:\\temp\\malware_sim.txt") if session.platform == 'windows'
345+
346+
# Simulate some activity
347+
(1..10).each do |i|
348+
break if should_terminate?
349+
print_status("Malware simulation activity #{i}/10")
350+
sleep(2)
351+
end
352+
end
353+
354+
# Main run method - calls the time bomb wrapper
355+
def run
356+
print_status("Starting malware simulation with time bomb functionality")
357+
run_with_time_bomb
358+
end
359+
360+
end

0 commit comments

Comments
 (0)