Commit eab58d17 authored by Luke Macken's avatar Luke Macken

Add some useful code to help with subprocess & logging from the python-script code.

https://github.com/wcmaier/python-script
parent f9c55435
# This file is part of pyrasite. # Some useful functions based on code from Will Maier's 'ideal Python script'
# https://github.com/wcmaier/python-script
# #
# pyrasite is free software: you can redistribute it and/or modify # Copyright (c) 2010 Will Maier <willmaier@ml1.net>
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# #
# pyrasite is distributed in the hope that it will be useful, # Permission to use, copy, modify, and distribute this software for any
# but WITHOUT ANY WARRANTY; without even the implied warranty of # purpose with or without fee is hereby granted, provided that the above
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # copyright notice and this permission notice appear in all copies.
# GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# along with pyrasite. If not, see <http://www.gnu.org/licenses/>. # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# Copyright (C) 2011 Red Hat, Inc. # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
import os # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
import socket # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import struct
import pyrasite import logging
import tempfile
import warnings
import traceback
import subprocess import subprocess
def run(cmd): def run(*args, **kwargs):
p = subprocess.Popen(cmd, shell=True, """Run a subprocess.
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
out, err = p.communicate()
if err:
warnings.warn(err)
return out.strip()
REVERSE_SHELL = """\
import sys, struct
sys.path.insert(0, "%s/../payloads/")
from StringIO import StringIO Returns a tuple (*process*, *stdout*, *stderr*). If the *communicate*
from _reverseconnection import ReverseConnection keyword argument is True, *stdout* and *stderr* will be strings.
Otherwise, they will be None. *process* is a :class:`subprocess.Popen`
instance. By default, the path to the script itself will be used as the
executable and *args* will be passed as arguments to it.
class ReversePythonShell(ReverseConnection): .. note::
host = 'localhost' The value of *executable* will be prepended to *args*.
port = %d
def on_command(self, s, cmd): :param args: arguments to be passed to :class:`subprocess.Popen`.
buffer = StringIO() :param kwargs: keyword arguments to be passed to :class:`subprocess.Popen`.
sys.stdout = buffer :param communicate: if True, call :meth:`subprocess.Popen.communicate` after creating the subprocess.
sys.stderr = buffer :param executable: if present, the path to a program to execute instead of this script.
output = ''
try:
exec(cmd)
output = buffer.getvalue()
except Exception, e:
output = str(e)
finally:
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
buffer.close()
header = struct.pack('<L', len(output))
s.sendall(header + output)
return True
ReversePythonShell().start()
"""
class PyrasiteIPC(object):
"""
An object that listens for connections from the reverse python shell payload,
and then allows you to run commands in the other process.
""" """
def __init__(self, pid): _kwargs = {
super(PyrasiteIPC, self).__init__() "stdin": subprocess.PIPE,
self.pid = pid "stdout": subprocess.PIPE,
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) "stderr": subprocess.PIPE,
self.sock.settimeout(5) "shell": True,
self.sock.bind(('localhost', 0)) }
self.sock.listen(20) communicate = kwargs.pop("communicate", True)
self.port = self.sock.getsockname()[1] _kwargs.update(kwargs)
self.client = None kwargs = _kwargs
self.running = True process = subprocess.Popen(args, **kwargs)
def inject(self): if communicate is True:
# Write out a reverse subprocess payload with a custom port stdout, stderr = process.communicate()
(fd, filename) = tempfile.mkstemp() else:
self.filename = filename stdout, stderr = None, None
tmp = os.fdopen(fd, 'w')
tmp.write(REVERSE_SHELL % ( return process, stdout, stderr
os.path.abspath(os.path.dirname(pyrasite.__file__)),
self.port))
tmp.close() def setup_logger(verbose=False):
# NullHandler was added in Python 3.1.
injector = pyrasite.CodeInjector(self.pid) try:
injector.inject(filename) NullHandler = logging.NullHandler
except AttributeError:
def listen(self): class NullHandler(logging.Handler):
(clientsocket, address) = self.sock.accept() def emit(self, record): pass
self.client = clientsocket
self.client.settimeout(3) # Add a do-nothing NullHandler to the module logger to prevent "No handlers
# could be found" errors. The calling code can still add other, more useful
def cmd(self, cmd): # handlers, or otherwise configure logging.
self.client.sendall(cmd + '\n') log = logging.getLogger('pyrasite')
try: log.addHandler(NullHandler())
header_data = self._recv_bytes(4)
if len(header_data) == 4: level = logging.INFO
msg_len = struct.unpack('<L', header_data)[0] if verbose:
data = self._recv_bytes(msg_len) level = logging.DEBUG
if len(data) == msg_len:
return data handler = logging.StreamHandler()
else: handler.setFormatter(logging.Formatter('%(message)s'))
print("Response doesn't match header len (%s) : %r" % ( handler.setLevel(level)
msg_len, data)) log.addHandler(handler)
except: log.setLevel(level)
traceback.print_exc()
self.close() return log
def _recv_bytes(self, n):
data = ''
while len(data) < n:
chunk = self.client.recv(n - len(data))
if chunk == '':
break
data += chunk
return data
def close(self):
os.unlink(self.filename)
if self.client:
self.client.sendall('exit\n')
self.client.close()
def __repr__(self):
return "<%s %s>" % (self.__class__.__name__, self.pid)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment