module RailsPwnerer::Base
extends Base
with gem-related functions
extends Base
with Rails-related functions
Public Class Methods
initializes the module in UNIX mode
# File lib/rails_pwnerer/base.rb 3 def self._setup_unix 4 #SUDO_PREFIX = 'sudo ' 5 end
initializes the module in Windows mode
# File lib/rails_pwnerer/base.rb 8 def self._setup_windows 9 #SUDO_PREFIX = '' 10 end
Public Instance Methods
erases a repository
# File lib/rails_pwnerer/base/atomics.rb 58 def atomic_erase(path, name) 59 main_file = File.join(path, name) + '.yml' 60 dup_file = File.join(path, name) + '.yml2' 61 [main_file, dup_file].each do |file| 62 File.delete file if File.exists? file 63 end 64 end
reads the data in a repository
# File lib/rails_pwnerer/base/atomics.rb 24 def atomic_read(path, name) 25 main_file = File.join(path, name) + '.yml' 26 dup_file = File.join(path, name) + '.yml2' 27 28 # choose the single good file or, if both are good, use the latest timestamp 29 # this works as long as the time on a box doesn't go back (it's ok to have it skewed) 30 results = [main_file, dup_file].map { |file| atomic_read_internal file } 31 results.sort { |a, b| b[1] <=> a[1] } 32 return results.first[0] 33 end
writes data to a repository
# File lib/rails_pwnerer/base/atomics.rb 36 def atomic_write(data, path, name, options = {}) 37 main_file = File.join(path, name) + '.yml' 38 dup_file = File.join(path, name) + '.yml2' 39 40 # append verification info at the end of the file to guard from incomplete writes 41 ts = Time.now 42 ts_checksum = Digest::MD5.hexdigest("#{ts.tv_sec}.#{ts.tv_usec}") 43 if options[:owner] 44 # secure the file 45 File.open(dup_file, 'w').close 46 uid = uid_for_username options[:owner] 47 gid = gid_for_username options[:owner] 48 File.chown uid, gid, dup_file 49 File.chmod options[:permissions] || 0660, dup_file 50 end 51 File.open(dup_file, 'w') { |f| YAML::dump [data, ts.tv_sec, ts.tv_usec, ts_checksum], f } 52 53 # move the file atomically to the main copy 54 FileUtils.mv(dup_file, main_file) 55 end
Package info for the best package matching a pattern or set of patterns.
Args:
patterns:: a String or Regexp, or an array of such Strings or Regexps
Returns a hash with the following keys:
:name:: the package name :version:: the package version
Each pattern is searched for in turn. Once there are packages matching a pattern, the
# File lib/rails_pwnerer/base/packages.rb 177 def best_package_matching(patterns) 178 patterns = [patterns] unless patterns.kind_of?(Enumerable) 179 patterns.each do |pattern| 180 packages = search_packages(pattern) 181 next if packages.empty? 182 best = packages.sort_by { |key, value| 183 [ 184 pattern.kind_of?(Regexp) ? ((key.index(pattern) == 0) ? 1 : 0) : 185 ((key == pattern) ? 1 : 0), 186 value.split(/[.-]/) 187 ] 188 }.last 189 return { :name => best.first, :version => best.last } 190 end 191 nil 192 end
check if the given path is the root of a Rails application
# File lib/rails_pwnerer/base/rails.rb 5 def check_rails_root(app_path = '.') 6 ['app', 'config', 'public', 'Rakefile'].all? do |path| 7 File.exists? File.join(app_path, path) 8 end 9 ['script/rails', 'config/database.yml'].any? do |path| 10 File.exists? File.join(app_path, path) 11 end 12 end
# File lib/rails_pwnerer/base/startup.rb 35 def control_boot_script(script_name, action = :restart) 36 path_to_script = "/etc/init.d/#{script_name}" 37 case action 38 when :stop 39 system "#{path_to_script} stop" 40 when :start 41 system "#{path_to_script} start" 42 when :restart 43 system "#{path_to_script} restart" 44 when :reload 45 system "#{path_to_script} reload" 46 end 47 end
returns information for each core in the system
# File lib/rails_pwnerer/base/cpus.rb 49 def cpu_cores 50 cpus = [] 51 Sys::CPU.processors do |p| 52 cpus << { 53 :freq => p.cpu_mhz.to_f, 54 :id => p.processor.to_i, 55 :cpu => (p.respond_to?(:physical_id) ? p.physical_id.to_i : 0), 56 :core => (p.respond_to?(:core_id) ? p.core_id.to_i : 0), 57 :num_cores => (p.respond_to?(:cpu_cores) ? p.cpu_cores.to_i : 1) 58 } 59 end 60 return cpus 61 end
gets the currently logged on user
# File lib/rails_pwnerer/base/dirs.rb 28 def current_user 29 Etc.getpwuid.name 30 end
checks if a gem exists
# File lib/rails_pwnerer/base/gems.rb 20 def gem_exists?(gem_name) 21 begin 22 output = `gem specification --local #{gem_name} 2> /dev/null` 23 return output =~ /^\-\-\- \!ruby\/object\:Gem\:\:Specification/ 24 rescue 25 # we get here if gem exits with an error code 26 return false 27 end 28 end
gets the GID associated with the username
# File lib/rails_pwnerer/base/dirs.rb 14 def gid_for_username(name) 15 passwd_entry = Etc.getpwnam(name) 16 return (passwd_entry.nil?) ? nil : passwd_entry.gid 17 end
gets the main group of the given user
# File lib/rails_pwnerer/base/dirs.rb 20 def group_for_username(name) 21 gid = gid_for_username(name) 22 return nil if gid.nil? 23 group_entry = Etc.getgrgid(gid) 24 return (group_entry.nil?) ? nil : group_entry.name 25 end
hooks a script into the boot sequence
# File lib/rails_pwnerer/base/startup.rb 19 def hook_boot_script(script_location, script_name = File.basename(script_location), options = {}) 20 # copy the script to /etc/init.d and chmod +x 21 target_script = path_to_boot_script script_name 22 if options[:symlink] 23 FileUtils.ln_s script_location, target_script, :force => true 24 exec_file = script_location 25 else 26 FileUtils.cp script_location, target_script 27 exec_file = target_script 28 end 29 File.chmod 0755, exec_file 30 31 # add to boot sequence 32 system "update-rc.d #{script_name} defaults" 33 end
TODO: use the Gem API instead of the command line
# File lib/rails_pwnerer/base/gems.rb 6 def install_gem(gem_name) 7 system "gem install #{gem_name}" 8 end
# File lib/rails_pwnerer/base/gems.rb 39 def install_gems(gem_names) 40 unroll_collection(gem_names) { |n| install_gem(n) } 41 end
Installs a package.
Args:
package_name:: the exact name of the package to be installed options:: accepts the following: :source:: if true, a source package is installed and built :skip_proxy:: if true, apt is instructed to bypass any proxy that might be
Returns true for success, false if something went wrong.
# File lib/rails_pwnerer/base/packages.rb 32 def install_package(package_name, options = {}) 33 return true if install_package_impl(package_name, options) 34 if options[:source] 35 if options[:no_proxy] 36 install_package package_name, options.merge(:source => false) 37 else 38 install_package package_name, options.merge(:no_proxy => true) 39 end 40 else 41 return false unless options[:no_proxy] 42 install_package package_name, options.merge(:no_proxy => true) 43 end 44 end
Internals for install_package.
# File lib/rails_pwnerer/base/packages.rb 59 def install_package_impl(package_name, options) 60 prefix, params = apt_params_for options 61 if options[:source] 62 with_temp_dir(:root => true) do 63 dep_cmd = "#{prefix} apt-get build-dep #{params} #{package_name}" 64 return false unless Kernel.system(dep_cmd) 65 fetch_cmd = "#{prefix} apt-get source -b #{params} #{package_name}" 66 return false unless Kernel.system(fetch_cmd) 67 deb_files = Dir.glob '*.deb', File::FNM_DOTMATCH 68 build_cmd = "#{prefix} dpkg -i #{deb_files.join(' ')}" 69 return false unless Kernel.system(build_cmd) 70 end 71 else 72 install_cmd = "#{prefix} apt-get install #{params} #{package_name}" 73 return false unless Kernel.system(install_cmd) 74 end 75 return true 76 end
Installs a package matching a pattern or list of patterns.
Args:
patterns:: same as for best_package_matching options:: same as for install_package
Returns true for success, false if something went wrong.
# File lib/rails_pwnerer/base/packages.rb 17 def install_package_matching(patterns, options = {}) 18 package = best_package_matching patterns 19 package ? install_package(package[:name], options) : false 20 end
# File lib/rails_pwnerer/base/process.rb 24 def kill_tree(pid) 25 Zerg::Support::Process.kill_tree pid 26 end
the distribution of the OS
# File lib/rails_pwnerer/base/dirs.rb 46 def os_distro 47 if RUBY_ARCH =~ /win/ 48 return "Windows" 49 else 50 File.open('/etc/issue', 'r') { |f| f.read }.split(/(\r|\n)+/, 2)[0] 51 end 52 end
returns the filesystem path to a boot script
# File lib/rails_pwnerer/base/startup.rb 9 def path_to_boot_script(script_name) 10 File.join '', 'etc', 'init.d', script_name 11 end
returns the filesystem path to the defaults used by a boot script (or nil if unsupported)
# File lib/rails_pwnerer/base/startup.rb 14 def path_to_boot_script_defaults(script_name) 15 File.join '', 'etc', 'default', script_name 16 end
locates the main file in a gem (used to locate the gem)
# File lib/rails_pwnerer/base/gems.rb 31 def path_to_gemdir 32 # TODO: use the rubygems API instead of this hack 33 34 `gem environment gemdir`.strip 35 end
returns information about a process
# File lib/rails_pwnerer/base/process.rb 6 def process_info(pid = nil) 7 info = Hash.new 8 Zerg::Support::ProcTable.ps.each do |process| 9 item = { :cmdline => process.cmdline, :pid => process.pid.to_s } 10 11 if pid.nil? 12 info[process.pid.to_s] = item 13 else 14 return item if item.pid.to_s == pid.to_s 15 end 16 end 17 if pid.nil? 18 return info 19 else 20 return nil 21 end 22 end
# File lib/rails_pwnerer/base/input.rb 11 def prompt_user_for_password(prompt, fail_prompt) 12 unless defined?(HighLine) 13 print "#{fail_prompt}\n" 14 return nil 15 end 16 17 HighLine.new.ask(prompt) { |question| question.echo = '' } 18 end
Removes a package.
Args:
package_name:: the exact name of the package to be installed
Returns true for success, false if something went wrong.
# File lib/rails_pwnerer/base/packages.rb 52 def remove_package(package_name, options = {}) 53 prefix, params = apt_params_for options 54 del_cmd = "#{prefix } apt-get remove #{params} #{package_name}" 55 Kernel.system(del_cmd) ? true : false 56 end
Searches for packages matching a name.
Args:
pattern:: a String or Regexp containing a pattern that should be matched by the package names
Returns a hash where the keys are matching package names, and the values are version numbers.
# File lib/rails_pwnerer/base/packages.rb 202 def search_packages(pattern) 203 packages = RailsPwnerer::Base.all_packages 204 Hash[packages.select { |key, value| 205 pattern.kind_of?(Regexp) ? (pattern =~ key) : key.index(pattern) 206 }.map { |key, value| 207 # apt-cache search sometimes leaves version numbers out 208 # Update the cache with version numbers. 209 if value.nil? 210 info = RailsPwnerer::Base.package_info_hash( 211
gets the UID associated with the username
# File lib/rails_pwnerer/base/dirs.rb 8 def uid_for_username(name) 9 passwd_entry = Etc.getpwnam(name) 10 return (passwd_entry.nil?) ? nil : passwd_entry.uid 11 end
unrolls a collection
# File lib/rails_pwnerer/base.rb 20 def unroll_collection(arg, &proc) 21 if arg.kind_of? String 22 yield arg 23 else 24 arg.each { |i| unroll_collection(i, &proc) } 25 end 26 end
update the metadata for all the gems
# File lib/rails_pwnerer/base/gems.rb 15 def update_gems() 16 system "gem update --system" 17 end
Updates the metadata for all the packages.
Options:
:skip_proxy:: if true, apt is instructed to bypass the proxy
Returns true for success, false if something went wrong.
# File lib/rails_pwnerer/base/packages.rb 128 def update_package_metadata(options = {}) 129 if update_package_metadata_impl(options) 130 # Reset the metadata cache. 131 RailsPwnerer::Base.instance_variable_set :@packages, nil 132 return true 133 end 134 135 return false if options[:skip_proxy] 136 update_package_metadata options.merge(:skip_proxy => true) 137 end
# File lib/rails_pwnerer/base/gems.rb 10 def upgrade_gem(gem_name) 11 system "gem update #{gem_name.nil ? '' : gem_name}" 12 end
# File lib/rails_pwnerer/base/gems.rb 43 def upgrade_gems(gem_names) 44 unroll_collection(gem_names) { |n| upgrade_gem(n) } 45 end
Executes the given block in the context of having new package sources.
Args:
source_url:: the source URL, e.g. http://security.ubuntu.com/ubuntu repositories:: the package repositories to use, e.g. ['main', 'universe'] options:: supports the following keys: :source:: if true, will use source-form packages from the new sources; by default, binary packages will be used
Returns the block’s return value.
After adding the new package source, the package metadata is refreshed, so the block can focus on installing new packages.
If the package source already exists, the given block is yielded without making any changes to the package configuration.
# File lib/rails_pwnerer/base/packages.rb 94 def with_package_source(source_url, source_repos = [], options = {}) 95 source_prefix = options[:source] ? 'deb-src' : 'deb' 96 source_patterns = [source_prefix, source_url] + source_repos 97 98 source_contents = File.read '/etc/apt/sources.list' 99 sources = source_contents.split(/(\r|\n)+/) 100 source_exists = sources.any? do |source_line| 101 source_frags = source_line.split(' ') 102 source_patterns.all? { |pattern| source_frags.any? { |frag| frag == pattern } } 103 end 104 105 unless source_exists 106 File.open('/etc/apt/sources.list', 'a') do |f| 107 f.write "#{source_prefix} #{source_url} #{source_repos.join(' ')}\n" 108 end 109 update_package_metadata 110 end 111 112 begin 113 yield 114 ensure 115 unless source_exists 116 File.open('/etc/apt/sources.list', 'w') { |f| f.write source_contents } 117 update_package_metadata 118 end 119 end 120 end
executes a block in a temporary directory
# File lib/rails_pwnerer/base/dirs.rb 33 def with_temp_dir(options = {}) 34 base_dir = if options[:root] 35 File.exists?('/tmp') ? '/tmp/' : '/' 36 else 37 './' 38 end 39 temp_dir = base_dir + "rbpwn_#{(Time.now.to_f * 1000).to_i}" 40 Dir.mkdir temp_dir 41 Dir.chdir(temp_dir) { yield } 42 FileUtils.rm_r temp_dir 43 end
Private Instance Methods
Builds apt-get parameters for an option hash.
Args:
options:: an option hash, as passed to install_package, update_package, or update_package_metadata
Returns prefix, args, where prefix is a prefix for the apt- command, and args is one or more command-line arguments.
# File lib/rails_pwnerer/base/packages.rb 155 def apt_params_for(options = {}) 156 prefix = 'env DEBIAN_FRONTEND=noninteractive ' 157 prefix += 'DEBIAN_PRIORITY=critical ' 158 prefix += 'DEBCONF_TERSE=yes ' 159 160 params = "-qq -y" 161 params += " -o Acquire::http::Proxy=false" if options[:skip_proxy] 162 return prefix, params 163 end
reads the content of one file returns nil if the file is corrupted, otherwise returns [file data, timestamp]
# File lib/rails_pwnerer/base/atomics.rb 10 def atomic_read_internal(file_name) 11 begin 12 raw_data = File.open(file_name, 'r') { |f| YAML::load f } 13 ts_checksum = Digest::MD5.hexdigest("#{raw_data[1]}.#{raw_data[2]}") 14 return [nil, Time.at(0, 0)] unless ts_checksum == raw_data[3] 15 return [raw_data[0], Time.at(raw_data[1], raw_data[2])] 16 rescue Exception 17 # fail if the YAML can't be processed or something else goes wrong 18 return [nil, Time.at(0, 0)] 19 end 20 end
Internals for update_package_metadata.
# File lib/rails_pwnerer/base/packages.rb 140 def update_package_metadata_impl(options) 141 prefix, params = apt_params_for options 142 Kernel.system("#{prefix} apt-get update #{params}") ? 143 true : false 144 end