Skip to content

Commit 055e88d

Browse files
committed
Land rapid7#8897, Rewrite timestomp command dispatcher to deal with arguments properly
2 parents 92f5290 + f707181 commit 055e88d

File tree

1 file changed

+155
-134
lines changed
  • lib/rex/post/meterpreter/ui/console/command_dispatcher/priv

1 file changed

+155
-134
lines changed

lib/rex/post/meterpreter/ui/console/command_dispatcher/priv/timestomp.rb

Lines changed: 155 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -2,141 +2,162 @@
22
require 'rex/post/meterpreter'
33

44
module Rex
5-
module Post
6-
module Meterpreter
7-
module Ui
8-
9-
###
10-
#
11-
# This class provides commands that interact with the timestomp feature set of
12-
# the privilege escalation extension.
13-
#
14-
###
15-
class Console::CommandDispatcher::Priv::Timestomp
16-
17-
Klass = Console::CommandDispatcher::Priv::Timestomp
18-
19-
include Console::CommandDispatcher
20-
21-
@@timestomp_opts = Rex::Parser::Arguments.new(
22-
"-m" => [ true, "Set the \"last written\" time of the file" ],
23-
"-a" => [ true, "Set the \"last accessed\" time of the file" ],
24-
"-c" => [ true, "Set the \"creation\" time of the file" ],
25-
"-e" => [ true, "Set the \"mft entry modified\" time of the file" ],
26-
"-z" => [ true, "Set all four attributes (MACE) of the file" ],
27-
"-f" => [ true, "Set the MACE of attributes equal to the supplied file" ],
28-
"-b" => [ false, "Set the MACE timestamps so that EnCase shows blanks" ],
29-
"-r" => [ false, "Set the MACE timestamps recursively on a directory" ],
30-
"-v" => [ false, "Display the UTC MACE values of the file" ],
31-
"-h" => [ false, "Help banner" ])
32-
33-
#
34-
# List of supported commands.
35-
#
36-
def commands
37-
{
38-
"timestomp" => "Manipulate file MACE attributes"
39-
}
40-
end
41-
42-
#
43-
# Name for this dispatcher.
44-
#
45-
def name
46-
"Priv: Timestomp"
47-
end
48-
49-
#
50-
# This command provides the same level of features that vinnie's command
51-
# line timestomp interface provides with a similar argument set.
52-
#
53-
def cmd_timestomp(*args)
54-
if (args.length < 2)
55-
print_line("\nUsage: timestomp OPTIONS file_path\n" +
56-
@@timestomp_opts.usage)
57-
return
58-
end
59-
60-
file_path = nil
61-
args.each { |a| file_path = a unless a[0] == "-" }
62-
63-
if file_path.nil?
64-
print_line("\nNo file_path specified.")
65-
return
66-
end
67-
68-
args.delete(file_path)
69-
70-
modified = nil
71-
accessed = nil
72-
creation = nil
73-
emodified = nil
74-
75-
@@timestomp_opts.parse(args) { |opt, idx, val|
76-
case opt
77-
when "-m"
78-
modified = str_to_time(val)
79-
when "-a"
80-
accessed = str_to_time(val)
81-
when "-c"
82-
creation = str_to_time(val)
83-
when "-e"
84-
emodified = str_to_time(val)
85-
when "-z"
86-
print_line("#{val}")
87-
modified = str_to_time(val)
88-
accessed = str_to_time(val)
89-
creation = str_to_time(val)
90-
emodified = str_to_time(val)
91-
when "-f"
92-
print_status("Setting MACE attributes on #{file_path} from #{val}")
93-
client.priv.fs.set_file_mace_from_file(file_path, val)
94-
when "-b"
95-
print_status("Blanking file MACE attributes on #{file_path}")
96-
client.priv.fs.blank_file_mace(file_path)
97-
when "-r"
98-
print_status("Blanking directory MACE attributes on #{file_path}")
99-
client.priv.fs.blank_directory_mace(file_path)
100-
when "-v"
101-
hash = client.priv.fs.get_file_mace(file_path)
102-
103-
print_line("Modified : #{hash['Modified']}")
104-
print_line("Accessed : #{hash['Accessed']}")
105-
print_line("Created : #{hash['Created']}")
106-
print_line("Entry Modified: #{hash['Entry Modified']}")
107-
when "-h"
108-
print_line("\nUsage: timestomp file_path OPTIONS\n" +
109-
@@timestomp_opts.usage)
110-
return
5+
module Post
6+
module Meterpreter
7+
module Ui
8+
###
9+
#
10+
# This class provides commands that interact with the timestomp feature set of
11+
# the privilege escalation extension.
12+
#
13+
###
14+
class Console::CommandDispatcher::Priv::Timestomp
15+
Klass = Console::CommandDispatcher::Priv::Timestomp
16+
17+
include Console::CommandDispatcher
18+
19+
@@timestomp_opts = Rex::Parser::Arguments.new(
20+
"-m" => [ true, "Set the \"last written\" time of the file" ],
21+
"-a" => [ true, "Set the \"last accessed\" time of the file" ],
22+
"-c" => [ true, "Set the \"creation\" time of the file" ],
23+
"-e" => [ true, "Set the \"mft entry modified\" time of the file" ],
24+
"-z" => [ true, "Set all four attributes (MACE) of the file" ],
25+
"-f" => [ true, "Set the MACE of attributes equal to the supplied file" ],
26+
"-b" => [ false, "Set the MACE timestamps so that EnCase shows blanks" ],
27+
"-r" => [ false, "Set the MACE timestamps recursively on a directory" ],
28+
"-v" => [ false, "Display the UTC MACE values of the file" ],
29+
"-h" => [ false, "Help banner" ]
30+
)
31+
32+
#
33+
# List of supported commands.
34+
#
35+
def commands
36+
{
37+
"timestomp" => "Manipulate file MACE attributes"
38+
}
39+
end
40+
41+
#
42+
# Name for this dispatcher.
43+
#
44+
def name
45+
"Priv: Timestomp"
46+
end
47+
48+
#
49+
# This command provides the same level of features that vinnie's command
50+
# line timestomp interface provides with a similar argument set.
51+
#
52+
def cmd_timestomp(*args)
53+
paths = []
54+
55+
modified = nil
56+
accessed = nil
57+
creation = nil
58+
emodified = nil
59+
60+
blank_file_mace = false
61+
blank_directory_mace = false
62+
get_file_mace = false
63+
help = false
64+
65+
@@timestomp_opts.parse(args) do |opt, _idx, val|
66+
case opt
67+
when "-m"
68+
modified = str_to_time(val)
69+
when "-a"
70+
accessed = str_to_time(val)
71+
when "-c"
72+
creation = str_to_time(val)
73+
when "-e"
74+
emodified = str_to_time(val)
75+
when "-z"
76+
modified = str_to_time(val)
77+
accessed = str_to_time(val)
78+
creation = str_to_time(val)
79+
emodified = str_to_time(val)
80+
when "-f"
81+
print_status("Setting MACE attributes on #{path} from #{val}")
82+
hash = client.priv.fs.get_file_mace(path)
83+
if hash
84+
modified = str_to_time(hash['Modified'])
85+
accessed = str_to_time(hash['Accessed'])
86+
creation = str_to_time(hash['Created'])
87+
emodified = str_to_time(hash['Entry Modified'])
88+
end
89+
when "-b"
90+
blank_file_mace = true
91+
when "-r"
92+
blank_directory_mace = true
93+
when "-v"
94+
get_file_mace = true
95+
when "-h"
96+
help = true
97+
when nil
98+
paths << val
99+
end
100+
end
101+
102+
if paths.empty?
103+
print_line("\nNo paths specified.")
104+
return nil
105+
end
106+
107+
if !(modified || accessed || creation || emodified ||
108+
blank_file_mace || blank_directory_mace || get_file_mace) || help
109+
print_line("\nUsage: timestomp <file(s)> OPTIONS\n" +
110+
@@timestomp_opts.usage)
111+
return nil
112+
end
113+
114+
paths.uniq.each do |path|
115+
# If any one of the four times were specified, change them.
116+
if modified || accessed || creation || emodified
117+
print_status("Setting specific MACE attributes on #{path}")
118+
client.priv.fs.set_file_mace(path, modified, accessed, creation, emodified)
119+
end
120+
121+
if blank_file_mace
122+
print_status("Blanking file MACE attributes on #{path}")
123+
client.priv.fs.blank_file_mace(path)
124+
end
125+
126+
if blank_directory_mace
127+
print_status("Blanking directory MACE attributes on #{path}")
128+
client.priv.fs.blank_directory_mace(path)
129+
end
130+
131+
if get_file_mace
132+
hash = client.priv.fs.get_file_mace(path)
133+
print_status("Showing MACE attributes for #{path}")
134+
print_line("Modified : #{hash['Modified']}")
135+
print_line("Accessed : #{hash['Accessed']}")
136+
print_line("Created : #{hash['Created']}")
137+
print_line("Entry Modified: #{hash['Entry Modified']}")
138+
end
139+
end
140+
end
141+
142+
protected
143+
144+
#
145+
# Converts a date/time in the form of MM/DD/YYYY HH24:MI:SS
146+
#
147+
def str_to_time(str) # :nodoc:
148+
unless str.nil?
149+
_r, mon, day, year, hour, min, sec =
150+
str.match("^(\\d+?)/(\\d+?)/(\\d+?) (\\d+?):(\\d+?):(\\d+?)$").to_a
151+
end
152+
153+
if str.nil? || mon.nil?
154+
raise ArgumentError, "Invalid date format, expected MM/DD/YYYY HH24:MI:SS (got #{str})"
155+
end
156+
157+
Time.mktime(year, mon, day, hour, min, sec, 0)
158+
end
159+
end
111160
end
112-
}
113-
114-
# If any one of the four times were specified, change them.
115-
if (modified or accessed or creation or emodified)
116-
print_status("Setting specific MACE attributes on #{file_path}")
117-
client.priv.fs.set_file_mace(file_path, modified, accessed,
118-
creation, emodified)
119-
end
120-
end
121-
122-
protected
123-
124-
#
125-
# Converts a date/time in the form of MM/DD/YYYY HH24:MI:SS
126-
#
127-
def str_to_time(str) # :nodoc:
128-
r, mon, day, year, hour, min, sec = str.match("^(\\d+?)/(\\d+?)/(\\d+?) (\\d+?):(\\d+?):(\\d+?)$").to_a
129-
130-
if (mon == nil)
131-
raise ArgumentError, "Invalid date format, expected MM/DD/YYYY HH24:MI:SS (got #{str})"
132161
end
133-
134-
Time.mktime(year, mon, day, hour, min, sec, 0)
135162
end
136-
137-
end
138-
139-
end
140-
end
141-
end
142163
end

0 commit comments

Comments
 (0)