forked from rapid7/metasploit-framework
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
409 additions
and
109 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,111 +1,116 @@ | ||
# -*- coding: binary -*- | ||
|
||
|
||
module Msf | ||
class Post | ||
module Linux | ||
module BusyBox | ||
|
||
include ::Msf::Post::Common | ||
include ::Msf::Post::File | ||
|
||
# Checks if the file exists in the target | ||
# | ||
# @param file_path [String] the target file path | ||
# @return [Boolean] true if files exists, false otherwise | ||
# @note Msf::Post::File#file? doesnt work because test -f is not available in busybox | ||
def busy_box_file_exist?(file_path) | ||
contents = read_file(file_path) | ||
if contents.nil? || contents.empty? | ||
return false | ||
end | ||
|
||
true | ||
end | ||
|
||
# Checks if the directory is writable in the target | ||
# | ||
# @param dir_path [String] the target directory path | ||
# @return [Boolean] true if target directory is writable, false otherwise | ||
def busy_box_is_writable_dir?(dir_path) | ||
res = false | ||
rand_str = Rex::Text.rand_text_alpha(16) | ||
file_path = "#{dir_path}/#{rand_str}" | ||
|
||
cmd_exec("echo #{rand_str}XXX#{rand_str} > #{file_path}") | ||
Rex::sleep(0.3) | ||
rcv = read_file(file_path) | ||
|
||
if rcv.include?("#{rand_str}XXX#{rand_str}") | ||
res = true | ||
end | ||
|
||
cmd_exec("rm -f #{file_path}") | ||
Rex::sleep(0.3) | ||
|
||
res | ||
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/) | ||
|
||
dirs.each do |d| | ||
return d if busy_box_is_writable_dir?(d) | ||
end | ||
|
||
nil | ||
end | ||
|
||
|
||
# Writes data to a file | ||
# | ||
# @param file_path [String] the file path to write on the target | ||
# @param data [String] the content to be written | ||
# @param prepend [Boolean] if true, prepend the data to the target file. Otherwise, overwrite | ||
# the target file | ||
# @return [Boolean] true if target file is writable and it was written. Otherwise, false. | ||
# @note BusyBox commands are limited and Msf::Post::File#write_file doesn't work here, because | ||
# of it is necessary to implement an specific method. | ||
def busy_box_write_file(file_path, data, prepend = false) | ||
if prepend | ||
dir = busy_box_writable_dir | ||
return false unless dir | ||
cmd_exec("cp -f #{file_path} #{dir}tmp") | ||
Rex::sleep(0.3) | ||
class Post | ||
module Linux | ||
module BusyBox | ||
include ::Msf::Post::Common | ||
include ::Msf::Post::File | ||
|
||
# | ||
# Checks if the file exists in the target | ||
# | ||
# @param file_path [String] the target file path | ||
# @return [Boolean] true if files exists, false otherwise | ||
# @note Msf::Post::File#file? doesnt work because test -f is not available in busybox | ||
# | ||
def busy_box_file_exist?(file_path) | ||
contents = read_file(file_path) | ||
if contents.nil? || contents.empty? | ||
return false | ||
end | ||
|
||
true | ||
end | ||
|
||
# | ||
# Checks if the directory is writable in the target | ||
# | ||
# @param dir_path [String] the target directory path | ||
# @return [Boolean] true if target directory is writable, false otherwise | ||
# | ||
def busy_box_is_writable_dir?(dir_path) | ||
res = false | ||
rand_str = Rex::Text.rand_text_alpha(16) | ||
file_path = "#{dir_path}/#{rand_str}" | ||
|
||
cmd_exec("echo #{rand_str}XXX#{rand_str} > #{file_path}") | ||
Rex.sleep(0.3) | ||
rcv = read_file(file_path) | ||
|
||
if rcv.include?("#{rand_str}XXX#{rand_str}") | ||
res = true | ||
end | ||
|
||
cmd_exec("rm -f #{file_path}") | ||
Rex.sleep(0.3) | ||
|
||
res | ||
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/] | ||
|
||
dirs.each do |d| | ||
return d if busy_box_is_writable_dir?(d) | ||
end | ||
|
||
nil | ||
end | ||
|
||
# | ||
# Writes data to a file | ||
# | ||
# @param file_path [String] the file path to write on the target | ||
# @param data [String] the content to be written | ||
# @param prepend [Boolean] if true, prepend the data to the target file. Otherwise, overwrite | ||
# the target file | ||
# @return [Boolean] true if target file is writable and it was written. Otherwise, false. | ||
# @note BusyBox commands are limited and Msf::Post::File#write_file doesn't work here, because | ||
# of it is necessary to implement an specific method. | ||
# | ||
def busy_box_write_file(file_path, data, prepend = false) | ||
if prepend | ||
dir = busy_box_writable_dir | ||
return false unless dir | ||
|
||
cmd_exec("cp -f #{file_path} #{dir}tmp") | ||
Rex.sleep(0.3) | ||
end | ||
|
||
rand_str = Rex::Text.rand_text_alpha(16) | ||
cmd_exec("echo #{rand_str} > #{file_path}") | ||
Rex.sleep(0.3) | ||
|
||
unless read_file(file_path).include?(rand_str) | ||
return false | ||
end | ||
|
||
cmd_exec("echo \"\"> #{file_path}") | ||
Rex.sleep(0.3) | ||
|
||
lines = data.lines.map(&:chomp) | ||
lines.each do |line| | ||
cmd_exec("echo #{line.chomp} >> #{file_path}") | ||
Rex.sleep(0.3) | ||
end | ||
|
||
if prepend | ||
cmd_exec("cat #{dir}tmp >> #{file_path}") | ||
Rex.sleep(0.3) | ||
|
||
cmd_exec("rm -f #{dir}tmp") | ||
Rex.sleep(0.3) | ||
end | ||
|
||
true | ||
end | ||
end | ||
end | ||
|
||
rand_str = Rex::Text.rand_text_alpha(16) | ||
cmd_exec("echo #{rand_str} > #{file_path}") | ||
Rex::sleep(0.3) | ||
|
||
unless read_file(file_path).include?(rand_str) | ||
return false | ||
end | ||
|
||
cmd_exec("echo \"\"> #{file_path}") | ||
Rex::sleep(0.3) | ||
|
||
lines = data.lines.map(&:chomp) | ||
lines.each do |line| | ||
cmd_exec("echo #{line.chomp} >> #{file_path}") | ||
Rex::sleep(0.3) | ||
end | ||
|
||
if prepend | ||
cmd_exec("cat #{dir}tmp >> #{file_path}") | ||
Rex::sleep(0.3) | ||
|
||
cmd_exec("rm -f #{dir}tmp") | ||
Rex::sleep(0.3) | ||
end | ||
|
||
true | ||
end | ||
end # Busybox | ||
end # Linux | ||
end # Post | ||
end # Msf | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
require 'spec_helper' | ||
|
||
RSpec.describe Msf::Post::Linux::Compile do | ||
subject do | ||
mod = Msf::Module.new | ||
mod.extend(Msf::Post::Linux::Compile) | ||
mod | ||
end | ||
|
||
describe '#get_compiler' do | ||
context 'when gcc is available' do | ||
it 'returns gcc' do | ||
allow(subject).to receive(:has_gcc?).and_return(true) | ||
expect(subject.get_compiler).to eq('gcc') | ||
end | ||
end | ||
|
||
context 'when clang is available' do | ||
it 'returns clang' do | ||
allow(subject).to receive(:has_gcc?).and_return(false) | ||
allow(subject).to receive(:has_clang?).and_return(true) | ||
expect(subject.get_compiler).to eq('clang') | ||
end | ||
end | ||
|
||
context 'when no compiler is available' do | ||
it 'returns nil' do | ||
allow(subject).to receive(:has_gcc?).and_return(false) | ||
allow(subject).to receive(:has_clang?).and_return(false) | ||
expect(subject.get_compiler).to be_nil | ||
end | ||
end | ||
|
||
describe '#live_compile?' do | ||
context 'when COMPILE is not Auto or True' do | ||
it 'returns false' do | ||
allow(subject).to receive(:datastore).and_return({ 'COMPILE' => 'False' }) | ||
expect(subject.live_compile?).to be false | ||
end | ||
end | ||
|
||
context 'when COMPILE is Auto or True' do | ||
it 'returns true if gcc is specified and available' do | ||
allow(subject).to receive(:datastore).and_return({ 'COMPILE' => 'Auto', 'COMPILER' => 'gcc' }) | ||
allow(subject).to receive(:has_gcc?).and_return(true) | ||
expect(subject.live_compile?).to be true | ||
end | ||
|
||
it 'returns true if clang is specified and available' do | ||
allow(subject).to receive(:datastore).and_return({ 'COMPILE' => 'Auto', 'COMPILER' => 'clang' }) | ||
allow(subject).to receive(:has_clang?).and_return(true) | ||
expect(subject.live_compile?).to be true | ||
end | ||
|
||
it 'returns true if Auto is specified and a compiler is available' do | ||
allow(subject).to receive(:datastore).and_return({ 'COMPILE' => 'Auto', 'COMPILER' => 'Auto' }) | ||
allow(subject).to receive(:get_compiler).and_return('gcc') | ||
expect(subject.live_compile?).to be true | ||
end | ||
|
||
it 'raises an error if the specified compiler is not available' do | ||
allow(subject).to receive(:datastore).and_return({ 'COMPILE' => 'True', 'COMPILER' => 'gcc' }) | ||
allow(subject).to receive(:has_gcc?).and_return(false) | ||
expect { subject.live_compile? }.to raise_error(Msf::Module::Failure::BadConfig, 'gcc is not installed. Set COMPILE False to upload a pre-compiled executable') | ||
end | ||
end | ||
|
||
describe '#upload_and_compile' do | ||
let(:source) { '/path/to/source.c' } | ||
let(:destination) { '/tmp/source.c' } | ||
let(:output) { '/tmp/output' } | ||
let(:session) { double('session') } | ||
|
||
before do | ||
allow(subject).to receive(:get_compiler).and_return('gcc') | ||
allow(subject).to receive(:session).and_return(session) | ||
end | ||
|
||
it 'uploads the source file and compiles it' do | ||
expect(subject).to receive(:upload_file).with(destination, source) | ||
expect(subject).to receive(:cmd_exec).with("gcc #{destination} -o #{output}") | ||
expect(subject).to receive(:write_file).and_return('/tmp/foo') | ||
allow(session).to receive(:type).and_return('meterpreter') | ||
|
||
subject.upload_and_compile(source, destination, output) | ||
end | ||
|
||
it 'raises an error if no compiler is available' do | ||
allow(subject).to receive(:get_compiler).and_return(nil) | ||
|
||
expect { subject.upload_and_compile(source, destination, output) }.to raise_error('No compiler available on target') | ||
end | ||
end | ||
|
||
describe '#strip_comments' do | ||
it 'removes comments from the source code' do | ||
source_code = <<-CODE | ||
// This is a single line comment | ||
int main() { | ||
/* This is a | ||
multi-line comment */ | ||
printf("Hello, world!"); | ||
return 0; | ||
} | ||
CODE | ||
|
||
expected_output = <<-CODE | ||
int main() { | ||
#{' '} | ||
printf("Hello, world!"); | ||
return 0; | ||
} | ||
CODE | ||
|
||
expect(subject.strip_comments(source_code)).to eq(expected_output) | ||
end | ||
end | ||
end | ||
end | ||
end |
File renamed without changes.
File renamed without changes.
Oops, something went wrong.