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

Linux post libs comments and specs #19682

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/msf/core/post/linux/busy_box.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ def busy_box_is_writable_dir?(dir_path)
end

# Checks some directories that usually are writable in devices running busybox
#
# @return [String] If the function finds a writable directory, it returns the path. Else it returns nil
#
def busy_box_writable_dir
dirs = %w(/etc/ /mnt/ /var/ /var/tmp/)

Expand Down
189 changes: 105 additions & 84 deletions lib/msf/core/post/linux/compile.rb
Original file line number Diff line number Diff line change
@@ -1,88 +1,109 @@
# -*- coding: binary -*-
module Msf
class Post
module Linux
module Compile
include ::Msf::Post::Common
include ::Msf::Post::File
include ::Msf::Post::Unix

def initialize(info = {})
super
register_options( [
OptEnum.new('COMPILE', [true, 'Compile on target', 'Auto', ['Auto', 'True', 'False']]),
OptEnum.new('COMPILER', [true, 'Compiler to use on target', 'Auto', ['Auto', 'gcc', 'clang']]),
], self.class)
end

def get_compiler
if has_gcc?
return 'gcc'
elsif has_clang?
return 'clang'
else
return nil
end
end

def live_compile?
return false unless %w{ Auto True }.include?(datastore['COMPILE'])

if datastore['COMPILER'] == 'gcc' && has_gcc?
vprint_good 'gcc is installed'
return true
elsif datastore['COMPILER'] == 'clang' && has_clang?
vprint_good 'clang is installed'
return true
elsif datastore['COMPILER'] == 'Auto' && get_compiler.present?
return true
end

unless datastore['COMPILE'] == 'Auto'
fail_with Module::Failure::BadConfig, "#{datastore['COMPILER']} is not installed. Set COMPILE False to upload a pre-compiled executable."
end

false
end

def upload_and_compile(path, data, compiler_args='')
write_file "#{path}.c", strip_comments(data)

compiler = datastore['COMPILER']
if datastore['COMPILER'] == 'Auto'
compiler = get_compiler
fail_with(Module::Failure::BadConfig, "Unable to find a compiler on the remote target.") unless compiler.present?
end

compiler_cmd = "#{compiler} -o '#{path}' '#{path}.c'"
if session.type == 'shell'
compiler_cmd = "PATH=\"$PATH:/usr/bin/\" #{compiler_cmd}"
end

unless compiler_args.to_s.blank?
compiler_cmd << " #{compiler_args}"
end

verification_token = Rex::Text.rand_text_alphanumeric(8)
success = cmd_exec("#{compiler_cmd} && echo #{verification_token}")&.include?(verification_token)

rm_f "#{path}.c"

unless success
message = "#{path}.c failed to compile."
# don't mention the COMPILE option if it was deregistered
message << ' Set COMPILE to False to upload a pre-compiled executable.' if options.include?('COMPILE')
fail_with Module::Failure::BadConfig, message
module Msf
class Post
module Linux
module Compile
include ::Msf::Post::Common
include ::Msf::Post::File
include ::Msf::Post::Unix

def initialize(info = {})
super
register_options([
OptEnum.new('COMPILE', [true, 'Compile on target', 'Auto', ['Auto', 'True', 'False']]),
OptEnum.new('COMPILER', [true, 'Compiler to use on target', 'Auto', ['Auto', 'gcc', 'clang']]),
], self.class)
end

# Determines the available compiler on the target system.
#
# @return [String, nil] The name of the compiler ('gcc' or 'clang') if available, or nil if none are found.
def get_compiler
if has_gcc?
return 'gcc'
elsif has_clang?
return 'clang'
else
return nil
end
end

# Checks whether the target supports live compilation based on the module's configuration and available tools.
#
# @return [Boolean] True if compilation is supported and a compiler is available; otherwise, False.
# @raise [Module::Failure::BadConfig] If the specified compiler is not installed and compilation is required.
def live_compile?
return false unless %w[Auto True].include?(datastore['COMPILE'])

if datastore['COMPILER'] == 'gcc' && has_gcc?
vprint_good 'gcc is installed'
return true
elsif datastore['COMPILER'] == 'clang' && has_clang?
vprint_good 'clang is installed'
return true
elsif datastore['COMPILER'] == 'Auto' && get_compiler.present?
return true
end

unless datastore['COMPILE'] == 'Auto'
fail_with Module::Failure::BadConfig, "#{datastore['COMPILER']} is not installed. Set COMPILE False to upload a pre-compiled executable."
end

false
end

#
# Uploads C code to the target, compiles it, and handles verification of the compiled binary.
#
# @param path [String] The path where the compiled binary will be created.
# @param data [String] The C code to compile.
# @param compiler_args [String] Additional arguments for the compiler command.
# @raise [Module::Failure::BadConfig] If compilation fails or no compiler is found.
#
def upload_and_compile(path, data, compiler_args = '')
write_file "#{path}.c", strip_comments(data)

compiler = datastore['COMPILER']
if datastore['COMPILER'] == 'Auto'
compiler = get_compiler
fail_with(Module::Failure::BadConfig, 'Unable to find a compiler on the remote target.') unless compiler.present?
end

compiler_cmd = "#{compiler} -o '#{path}' '#{path}.c'"
if session.type == 'shell'
compiler_cmd = "PATH=\"$PATH:/usr/bin/\" #{compiler_cmd}"
end

unless compiler_args.to_s.blank?
compiler_cmd << " #{compiler_args}"
end

verification_token = Rex::Text.rand_text_alphanumeric(8)
success = cmd_exec("#{compiler_cmd} && echo #{verification_token}")&.include?(verification_token)

rm_f "#{path}.c"

unless success
message = "#{path}.c failed to compile."
# don't mention the COMPILE option if it was deregistered
message << ' Set COMPILE to False to upload a pre-compiled executable.' if options.include?('COMPILE')
fail_with Module::Failure::BadConfig, message
end

chmod path
end

#
# Strips comments from C source code.
#
# @param c_code [String] The C source code.
# @return [String] The C code with comments removed.
#
def strip_comments(c_code)
c_code.gsub(%r{/\*.*?\*/}m, '').gsub(%r{^\s*//.*$}, '')
end
end
end

chmod path
end

def strip_comments(c_code)
c_code.gsub(%r{/\*.*?\*/}m, '').gsub(%r{^\s*//.*$}, '')
end

end # Compile
end # Linux
end # Post
end # Msf
end
Loading