Skip to content

Commit a3a331a

Browse files
authored
Land rapid7#18926, updates new sessions to have a consistent local file system API
2 parents 05a7698 + 64831b6 commit a3a331a

File tree

7 files changed

+331
-238
lines changed

7 files changed

+331
-238
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# -*- coding: binary -*-
2+
3+
module Msf
4+
module Ui
5+
module Console
6+
module CommandDispatcher
7+
###
8+
#
9+
# Generic file system commands
10+
#
11+
###
12+
class LocalFileSystem
13+
include Rex::Ui::Text::DispatcherShell::CommandDispatcher
14+
include Msf::Ui::Console::LocalFileSystem
15+
16+
#
17+
# List of supported commands.
18+
#
19+
# @return [Hash]
20+
def commands
21+
local_fs_commands
22+
end
23+
24+
# @param [String]
25+
def name
26+
'Local File System'
27+
end
28+
29+
def unknown_command(cmd, line)
30+
status = super
31+
32+
status
33+
end
34+
end
35+
end
36+
end
37+
end
38+
end
Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
# -*- coding: binary -*-
2+
3+
module Msf
4+
module Ui
5+
module Console
6+
###
7+
#
8+
# This module provides commands for the local file system
9+
#
10+
###
11+
module LocalFileSystem
12+
#
13+
# Options for the lls command
14+
#
15+
@@lls_opts = Rex::Parser::Arguments.new(
16+
'-h' => [ false, 'Help banner' ],
17+
'-S' => [ true, 'Search string on filename (as regular expression)' ],
18+
'-t' => [ false, 'Sort by time' ],
19+
'-s' => [ false, 'Sort by size' ],
20+
'-r' => [ false, 'Reverse sort order' ]
21+
)
22+
23+
#
24+
# List of supported local commands.
25+
#
26+
# @return [Hash] Hash of local commands
27+
def local_fs_commands
28+
{
29+
'getlwd' => 'Print local working directory (alias for lpwd)',
30+
'lcat' => 'Read the contents of a local file to the screen',
31+
'lcd' => 'Change local working directory',
32+
'lmkdir' => 'Create new directory on local machine',
33+
'lpwd' => 'Print local working directory',
34+
'lls' => 'List local files',
35+
'ldir' => 'List local files (alias for lls)'
36+
}
37+
end
38+
39+
#
40+
# List local files
41+
#
42+
# @param [Array] args
43+
# @return [Rex::Text::Table] The results lls command
44+
def cmd_lls(*args)
45+
# Set Defaults
46+
path = ::Dir.pwd
47+
sort = 'Name'
48+
order = :forward
49+
search_term = nil
50+
51+
# Parse the args
52+
@@lls_opts.parse(args) do |opt, _idx, val|
53+
case opt
54+
# Sort options
55+
when '-s'
56+
sort = 'Size'
57+
when '-t'
58+
sort = 'Last modified'
59+
# Output options
60+
when '-r'
61+
order = :reverse
62+
# Search
63+
when '-S'
64+
search_term = val
65+
if search_term.nil?
66+
print_error('Enter a search term')
67+
return true
68+
else
69+
search_term = /#{search_term}/nmi
70+
end
71+
# Help and path
72+
when '-h'
73+
cmd_lls_help
74+
return 0
75+
when nil
76+
path = val
77+
end
78+
end
79+
80+
list_local_path(path, sort, order, search_term)
81+
end
82+
83+
#
84+
# Help output for lss command
85+
#
86+
# @return [Rex::Parser::Arguments]
87+
def cmd_lls_help
88+
print_line 'Usage: lls [options]'
89+
print_line
90+
print_line 'Lists contents of a local directory or file info'
91+
print_line @@lls_opts.usage
92+
end
93+
94+
#
95+
# Alias the lls command to dir, for those of us who have windows muscle-memory
96+
#
97+
alias cmd_ldir cmd_lls
98+
99+
#
100+
# Change the local working directory.
101+
#
102+
# @param [Array] args
103+
# @return [TrueClass]
104+
def cmd_lcd(*args)
105+
if args.empty?
106+
print_line('Usage: lcd directory')
107+
return true
108+
end
109+
110+
::Dir.chdir(args[0])
111+
112+
true
113+
end
114+
115+
#
116+
# Tab completion for the lcd command
117+
#
118+
# @param [String] str
119+
# @param [Array] words
120+
def cmd_lcd_tabs(str, words)
121+
tab_complete_directory(str, words)
122+
end
123+
124+
alias cmd_lls_tabs cmd_lcd_tabs
125+
126+
#
127+
# Get list local path information for lls command
128+
#
129+
# @param [String] path
130+
# @param [String] sort
131+
# @param [Symbol] order
132+
# @param [nil] search_term
133+
# @return [Rex::Text::Table, String] The results lcd command
134+
def list_local_path(path, sort, order, search_term = nil)
135+
# Single file as path
136+
unless ::File.directory?(path)
137+
perms = pretty_perms(path)
138+
stat = ::File.stat(path)
139+
print_line("#{perms} #{stat.size} #{stat.ftype[0, 3]} #{stat.mtime} #{path}")
140+
return
141+
end
142+
143+
# Enumerate each item...
144+
# No need to sort as Table will do it for us
145+
columns = [ 'Mode', 'Size', 'Type', 'Last modified', 'Name' ]
146+
tbl = Rex::Text::Table.new(
147+
'Header' => "Listing Local: #{path}",
148+
'SortIndex' => columns.index(sort),
149+
'SortOrder' => order,
150+
'Columns' => columns
151+
)
152+
153+
items = 0
154+
files = ::Dir.entries(path)
155+
156+
files.each do |file|
157+
file_path = ::File.join(path, file)
158+
159+
perms = pretty_perms(file_path)
160+
stat = ::File.stat(file_path)
161+
162+
row = [
163+
perms || '',
164+
stat.size ? stat.size.to_s : '',
165+
stat.ftype ? stat.ftype[0, 3] : '',
166+
stat.mtime || '',
167+
file
168+
]
169+
if file != '.' && file != '..' && (row.join(' ') =~ /#{search_term}/)
170+
tbl << row
171+
items += 1
172+
end
173+
end
174+
if items > 0
175+
print_line(tbl.to_s)
176+
else
177+
print_line("No entries exist in #{path}")
178+
end
179+
end
180+
181+
#
182+
# Reads the contents of a local file and prints them to the screen.
183+
#
184+
# @param [Array] args
185+
# @return [TrueClass]
186+
def cmd_lcat(*args)
187+
if args.empty? || args.include?('-h') || args.include?('--help')
188+
print_line('Usage: lcat file')
189+
return true
190+
end
191+
192+
path = args[0]
193+
path = ::File.expand_path(path) if path =~ path_expand_regex
194+
195+
if ::File.stat(path).directory?
196+
print_error("#{path} is a directory")
197+
else
198+
fd = ::File.new(path, 'rb')
199+
begin
200+
print(fd.read) until fd.eof?
201+
# EOFError is raised if file is empty, do nothing, just catch
202+
rescue EOFError
203+
end
204+
fd.close
205+
end
206+
207+
true
208+
end
209+
210+
#
211+
# Tab completion for the lcat command
212+
#
213+
# @param [Object] str
214+
# @param [Object] words
215+
# @return [Array] List of matches
216+
def cmd_lcat_tabs(str, words)
217+
tab_complete_filenames(str, words)
218+
end
219+
220+
#
221+
# Create new directory on local machine
222+
#
223+
# @param [Array] args
224+
# @return [Array]
225+
def cmd_lmkdir(*args)
226+
if args.empty?
227+
print_line('Usage: lmkdir </path/to/directory>')
228+
return
229+
end
230+
231+
args.each do |path|
232+
::FileUtils.mkdir_p(path)
233+
print_line("Directory '#{path}' created successfully.")
234+
rescue ::StandardError => e
235+
print_error("Error creating #{path} directory: #{e}")
236+
end
237+
end
238+
239+
#
240+
# Display the local working directory.
241+
#
242+
# @param [Array] args
243+
# @return [TrueClass]
244+
def cmd_lpwd(*args)
245+
print_line(::Dir.pwd)
246+
true
247+
end
248+
249+
alias cmd_getlwd cmd_lpwd
250+
251+
#
252+
# Code from prettymode in lib/rex/post/file_stat.rb
253+
# adapted for local file usage
254+
#
255+
# @param [Object] path
256+
# @return [String]
257+
def pretty_perms(path)
258+
m = ::File.stat(path).mode
259+
om = '%04o' % m
260+
perms = ''
261+
262+
3.times do
263+
perms = ((m & 0o1) == 0o1 ? 'x' : '-') + perms
264+
perms = ((m & 0o2) == 0o2 ? 'w' : '-') + perms
265+
perms = ((m & 0o4) == 0o4 ? 'r' : '-') + perms
266+
m >>= 3
267+
end
268+
269+
"#{om}/#{perms}"
270+
end
271+
272+
private
273+
274+
# @return [Regexp]
275+
def path_expand_regex
276+
if shell.session.platform == 'windows'
277+
/%(\w*)%/
278+
else
279+
/\$(([A-Za-z0-9_]+)|\{([A-Za-z0-9_]+)\})|^~/
280+
end
281+
end
282+
end
283+
end
284+
end
285+
end

0 commit comments

Comments
 (0)