Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expand environment variables on Linux #15862

Merged
merged 5 commits into from Nov 15, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
42 changes: 31 additions & 11 deletions lib/rex/post/meterpreter/extensions/stdapi/fs/file.rb
Expand Up @@ -123,28 +123,48 @@ def File.basename(*a)

#
# Expands a file path, substituting all environment variables, such as
# %TEMP%.
# %TEMP% on Windows or $HOME on Unix
#
# Examples:
# client.fs.file.expand_path("%appdata%")
# # => "C:\\Documents and Settings\\user\\Application Data"
# client.fs.file.expand_path("~")
# # => "/home/user"
# client.fs.file.expand_path("$HOME/dir")
# # => "/home/user/dir"
# client.fs.file.expand_path("asdf")
# # => "asdf"
#
# NOTE: This method is fairly specific to Windows. It has next to no relation
# to the ::File.expand_path method! In particular, it does *not* do ~
# expansion or environment variable expansion on non-Windows systems. For
# these reasons, this method may be deprecated in the future. Use it with
# caution.
#
def File.expand_path(path)
request = Packet.create_request(COMMAND_ID_STDAPI_FS_FILE_EXPAND_PATH)
case client.platform
when 'osx', 'freebsd', 'bsd', 'linux', 'android', 'apple_ios'
# For unix-based systems, do some of the work here
# First check for ~
path_components = path.split(separator)
if path_components.length > 0 && path_components[0] == '~'
path_components[0] = '$HOME'
path = path_components.join(separator)
end

request.add_tlv(TLV_TYPE_FILE_PATH, client.unicode_filter_decode( path ))
# Now find the environment variables we'll need from the client
env_regex = /\$(?:([A-Za-z0-9_]+)|\{([A-Za-z0-9_]+)\})/
matches = path.to_enum(:scan, env_regex).map { Regexp.last_match }
env_vars = matches.map { |match| (match[1] || match[2]).to_s }.uniq

response = client.send_request(request)
# Retrieve them
env_vals = client.sys.config.getenvs(*env_vars)

return client.unicode_filter_encode(response.get_tlv_value(TLV_TYPE_FILE_PATH))
# Now fill them in
path.gsub(env_regex) { |_z| envvar = $1; envvar = $2 if envvar == nil; env_vals[envvar] }
else
request = Packet.create_request(COMMAND_ID_STDAPI_FS_FILE_EXPAND_PATH)

request.add_tlv(TLV_TYPE_FILE_PATH, client.unicode_filter_decode( path ))

response = client.send_request(request)

return client.unicode_filter_encode(response.get_tlv_value(TLV_TYPE_FILE_PATH))
end
end


Expand Down
55 changes: 37 additions & 18 deletions lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb
Expand Up @@ -23,8 +23,14 @@ class Console::CommandDispatcher::Stdapi::Fs

CHECKSUM_ALGORITHMS = %w{ md5 sha1 }
private_constant :CHECKSUM_ALGORITHMS
PATH_EXPAND_REGEX = /\%(\w*)\%/
private_constant :PATH_EXPAND_REGEX

private def path_expand_regex
if client.platform == 'windows'
/\%(\w*)\%/
else
/\$(([A-Za-z0-9_]+)|\{([A-Za-z0-9_]+)\})|^~/
end
end

#
# Options for the download command.
Expand Down Expand Up @@ -172,6 +178,7 @@ def cmd_search(*args)
return
when "-d"
root = val
root = client.fs.file.expand_path(root) if root =~ path_expand_regex
when "-f"
globs << val
when "-r"
Expand Down Expand Up @@ -271,10 +278,14 @@ def cmd_cat(*args)
return true
end

if (client.fs.file.stat(args[0]).directory?)
print_error("#{args[0]} is a directory")
path = args[0]
path = client.fs.file.expand_path(path) if path =~ path_expand_regex


if (client.fs.file.stat(path).directory?)
print_error("#{path} is a directory")
else
fd = client.fs.file.new(args[0], "rb")
fd = client.fs.file.new(path, "rb")
begin
until fd.eof?
print(fd.read)
Expand Down Expand Up @@ -303,8 +314,8 @@ def cmd_cd(*args)
print_line("Usage: cd directory")
return true
end
if args[0] =~ PATH_EXPAND_REGEX
client.fs.dir.chdir(client.fs.file.expand_path(args[0].upcase))
if args[0] =~ path_expand_regex
client.fs.dir.chdir(client.fs.file.expand_path(args[0]))
else
client.fs.dir.chdir(args[0])
end
Expand Down Expand Up @@ -352,6 +363,7 @@ def cmd_checksum(*args)
end

args.each do |filepath|
filepath = client.fs.file.expand_path(filepath) if filepath =~ path_expand_regex
checksum = client.fs.file.send(algorithm, filepath)
print_line("#{Rex::Text.to_hex(checksum, '')} #{filepath}")
end
Expand Down Expand Up @@ -380,7 +392,7 @@ def cmd_rm(*args)
end

args.each do |file_path|
file_path = client.fs.file.expand_path(file_path) if file_path =~ PATH_EXPAND_REGEX
file_path = client.fs.file.expand_path(file_path) if file_path =~ path_expand_regex
client.fs.file.rm(file_path)
end

Expand All @@ -400,9 +412,9 @@ def cmd_mv(*args)
return true
end
old_path = args[0]
old_path = client.fs.file.expand_path(old_path) if old_path =~ PATH_EXPAND_REGEX
old_path = client.fs.file.expand_path(old_path) if old_path =~ path_expand_regex
new_path = args[1]
new_path = client.fs.file.expand_path(new_path) if new_path =~ PATH_EXPAND_REGEX
new_path = client.fs.file.expand_path(new_path) if new_path =~ path_expand_regex
client.fs.file.mv(old_path, new_path)
return true
end
Expand All @@ -423,9 +435,9 @@ def cmd_cp(*args)
return true
end
old_path = args[0]
old_path = client.fs.file.expand_path(old_path) if old_path =~ PATH_EXPAND_REGEX
old_path = client.fs.file.expand_path(old_path) if old_path =~ path_expand_regex
new_path = args[1]
new_path = client.fs.file.expand_path(new_path) if new_path =~ PATH_EXPAND_REGEX
new_path = client.fs.file.expand_path(new_path) if new_path =~ path_expand_regex
client.fs.file.cp(old_path, new_path)
return true
end
Expand All @@ -443,7 +455,7 @@ def cmd_chmod(*args)
return true
end
file_path = args[1]
file_path = client.fs.file.expand_path(file_path) if file_path =~ PATH_EXPAND_REGEX
file_path = client.fs.file.expand_path(file_path) if file_path =~ path_expand_regex
client.fs.file.chmod(file_path, args[0].to_i(8))
return true
end
Expand Down Expand Up @@ -523,6 +535,8 @@ def cmd_download(*args)

# Go through each source item and download them
src_items.each { |src|

src = client.fs.file.expand_path(src) if src =~ path_expand_regex
glob = nil
if client.fs.file.is_glob?(src)
glob = ::File.basename(src)
Expand Down Expand Up @@ -607,15 +621,18 @@ def cmd_edit(*args)
meterp_temp.binmode
temp_path = meterp_temp.path

client_path = args[0]
client_path = client.fs.file.expand_path(client_path) if client_path =~ path_expand_regex

# Try to download the file, but don't worry if it doesn't exist
client.fs.file.download_file(temp_path, args[0]) rescue nil
client.fs.file.download_file(temp_path, client_path) rescue nil

# Spawn the editor (default to vi)
editor = Rex::Compat.getenv('EDITOR') || 'vi'

# If it succeeds, upload it to the remote side.
if (system("#{editor} #{temp_path}") == true)
client.fs.file.upload_file(args[0], temp_path)
client.fs.file.upload_file(client_path, temp_path)
end

# Get rid of that pesky temporary file
Expand Down Expand Up @@ -746,7 +763,7 @@ def cmd_ls(*args)
return 0
when nil
path = val
path = client.fs.file.expand_path(path) if path =~ PATH_EXPAND_REGEX
path = client.fs.file.expand_path(path) if path =~ path_expand_regex
end
}

Expand Down Expand Up @@ -923,7 +940,7 @@ def cmd_mkdir(*args)
end

args.each { |dir_path|
dir_path = client.fs.file.expand_path(dir_path) if dir_path =~ PATH_EXPAND_REGEX
dir_path = client.fs.file.expand_path(dir_path) if dir_path =~ path_expand_regex
print_line("Creating directory: #{dir_path}")
client.fs.dir.mkdir(dir_path)
}
Expand Down Expand Up @@ -952,7 +969,7 @@ def cmd_rmdir(*args)
end

args.each { |dir_path|
dir_path = client.fs.file.expand_path(dir_path) if dir_path =~ PATH_EXPAND_REGEX
dir_path = client.fs.file.expand_path(dir_path) if dir_path =~ path_expand_regex
print_line("Removing directory: #{dir_path}")
client.fs.dir.rmdir(dir_path)
}
Expand Down Expand Up @@ -1008,6 +1025,8 @@ def cmd_upload(*args)
dest = last
end

dest = client.fs.file.expand_path(dest) if dest =~ path_expand_regex

# Go through each source item and upload them
src_items.each { |src|
src = ::File.expand_path(src)
Expand Down
36 changes: 36 additions & 0 deletions test/modules/post/test/file.rb
Expand Up @@ -185,6 +185,42 @@ def test_binary_files
end
end

def test_path_expansion_nix
unless session.platform =~ /win/i
it "should expand home" do
home1 = expand_path('~')
home2 = expand_path('$HOME')
home1 == home2 && home1.length > 0
end

it "non-isolated tilde should not expand" do
s = '~a'
result = expand_path(s)
s == result
end

it "mid-string tilde should not expand" do
s = '/home/~'
result = expand_path(s)
s == result
end

it "env vars with invalid naming should not expand" do
s = 'no environment $ variables /here'
result = expand_path(s)
s == result
end

it "should expand multiple variables" do
result = expand_path('/blah/$HOME/test/$USER')
home = expand_path('$HOME')
user = expand_path('$USER')
expected = "/blah/#{home}/test/#{user}"
result == expected
end
end
end

def cleanup
vprint_status("Cleanup: changing working directory back to #{@old_pwd}")
cd(@old_pwd)
Expand Down