module RailsPwnerer::Base

extends Base with gem-related functions

extends Base with Rails-related functions

Public Class Methods

_setup_unix() click to toggle source

initializes the module in UNIX mode

  # File lib/rails_pwnerer/base.rb
3 def self._setup_unix
4   #SUDO_PREFIX = 'sudo '
5 end
_setup_windows() click to toggle source

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

atomic_erase(path, name) click to toggle source

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
atomic_read(path, name) click to toggle source

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
atomic_write(data, path, name, options = {}) click to toggle source

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
best_package_matching(patterns) click to toggle source

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_rails_root(app_path = '.') click to toggle source

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
control_boot_script(script_name, action = :restart) click to toggle source
   # 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
cpu_cores() click to toggle source

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
current_user() click to toggle source

gets the currently logged on user

   # File lib/rails_pwnerer/base/dirs.rb
28 def current_user
29   Etc.getpwuid.name
30 end
gem_exists?(gem_name) click to toggle source

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
gid_for_username(name) click to toggle source

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
group_for_username(name) click to toggle source

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
hook_boot_script(script_location, script_name = File.basename(script_location), options = {}) click to toggle source

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
install_gem(gem_name) click to toggle source

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
install_gems(gem_names) click to toggle source
   # File lib/rails_pwnerer/base/gems.rb
39 def install_gems(gem_names)
40   unroll_collection(gem_names) { |n| install_gem(n) }
41 end
install_package(package_name, options = {}) click to toggle source

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
install_package_impl(package_name, options) click to toggle source

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
install_package_matching(patterns, options = {}) click to toggle source

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
kill_tree(pid) click to toggle source
   # File lib/rails_pwnerer/base/process.rb
24 def kill_tree(pid)
25   Zerg::Support::Process.kill_tree pid
26 end
os_distro() click to toggle source

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
path_to_boot_script(script_name) click to toggle source

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
path_to_boot_script_defaults(script_name) click to toggle source

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
path_to_gemdir() click to toggle source

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
process_info(pid = nil) click to toggle source

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
prompt_user_for_password(prompt, fail_prompt) click to toggle source
   # 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
remove_package(package_name, options = {}) click to toggle source

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
search_packages(pattern) click to toggle source

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           
uid_for_username(name) click to toggle source

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
unroll_collection(arg) { |arg| ... } click to toggle source

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_gems() click to toggle source

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
update_package_metadata(options = {}) click to toggle source

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
upgrade_gem(gem_name) click to toggle source
   # File lib/rails_pwnerer/base/gems.rb
10 def upgrade_gem(gem_name)
11   system "gem update #{gem_name.nil ? '' : gem_name}"
12 end
upgrade_gems(gem_names) click to toggle source
   # File lib/rails_pwnerer/base/gems.rb
43 def upgrade_gems(gem_names)
44   unroll_collection(gem_names) { |n| upgrade_gem(n) }
45 end
with_package_source(source_url, source_repos = [], options = {}) { || ... } click to toggle source

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
with_temp_dir(options = {}) { || ... } click to toggle source

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

apt_params_for(options = {}) click to toggle source

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
atomic_read_internal(file_name) click to toggle source

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
update_package_metadata_impl(options) click to toggle source

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