Squashed commit of the following:

commit df6eef12147a294d7f198d057c27e87ed4ffbeb3
Author: MM <gaspmat@gmail.com>
Date:   Tue Mar 20 18:01:50 2012 +0100

    ps support for linux meterpreter

[Closes #250]
unstable
MM 2012-05-15 16:57:17 -06:00 committed by James Lee
parent 6bbf018423
commit 5d7190e8cb
5 changed files with 393 additions and 85 deletions

View File

@ -1,7 +1,7 @@
#include "precomp.h"
#include "ps.h" // include the code for listing proceses
#ifdef _WIN32
#include "ps.h" // include the code for listing proceses
#include "./../session.h"
#include "in-mem-exe.h" /* include skapetastic in-mem exe exec */
@ -859,8 +859,10 @@ DWORD request_sys_process_get_processes( Remote * remote, Packet * packet )
#else
DWORD result = ERROR_NOT_SUPPORTED;
Packet * response = packet_create_response( packet );
packet_transmit_response( result, remote, response );
if (response) {
result = ps_list_linux( response );
packet_transmit_response( result, remote, response );
}
#endif
return result;

View File

@ -1,5 +1,8 @@
#include "precomp.h"
#include "ps.h"
#ifdef _WIN32
#include "./../session.h"
#include "./../../../../../common/arch/win/i386/base_inject.h"
@ -394,64 +397,7 @@ BOOL ps_getusername( DWORD pid, char * cpUserName, DWORD dwUserNameSize )
return success;
}
/*
* Add the details of a process to the response.
*/
VOID ps_addresult( Packet * response, DWORD dwPid, DWORD dwParentPid, char * cpExeName, char * cpExePath, char * cpUserName, DWORD dwProcessArch )
{
Tlv entries[7] = {0};
DWORD dwSessionId = 0;
do
{
if( !response )
break;
dwSessionId = session_id( dwPid );
dwPid = htonl( dwPid );
entries[0].header.type = TLV_TYPE_PID;
entries[0].header.length = sizeof( DWORD );
entries[0].buffer = (PUCHAR)&dwPid;
if( !cpExeName )
cpExeName = "";
entries[1].header.type = TLV_TYPE_PROCESS_NAME;
entries[1].header.length = (DWORD)strlen( cpExeName ) + 1;
entries[1].buffer = cpExeName;
if( !cpExePath )
cpExePath = "";
entries[2].header.type = TLV_TYPE_PROCESS_PATH;
entries[2].header.length = (DWORD)strlen( cpExePath ) + 1;
entries[2].buffer = cpExePath;
if( !cpUserName )
cpUserName = "";
entries[3].header.type = TLV_TYPE_USER_NAME;
entries[3].header.length = (DWORD)strlen( cpUserName ) + 1;
entries[3].buffer = cpUserName;
dwProcessArch = htonl( dwProcessArch );
entries[4].header.type = TLV_TYPE_PROCESS_ARCH;
entries[4].header.length = sizeof( DWORD );
entries[4].buffer = (PUCHAR)&dwProcessArch;
dwParentPid = htonl( dwParentPid );
entries[5].header.type = TLV_TYPE_PARENT_PID;
entries[5].header.length = sizeof( DWORD );
entries[5].buffer = (PUCHAR)&dwParentPid;
dwSessionId = htonl( dwSessionId );
entries[6].header.type = TLV_TYPE_PROCESS_SESSION;
entries[6].header.length = sizeof( DWORD );
entries[6].buffer = (PUCHAR)&dwSessionId;
packet_add_tlv_group( response, TLV_TYPE_PROCESS_GROUP, entries, 7 );
} while(0);
}
/*
* Generate a process list via the kernel32!CreateToolhelp32Snapshot method. Works on Windows 2000 and above.
@ -632,3 +578,336 @@ DWORD ps_list_via_brute( Packet * response )
return result;
}
#else // linux part
/*
* linux specific ps command and structures
*/
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#define NAME "Name:\t"
#define STATE "State:\t"
#define UID "Uid:\t"
#define PPID "PPid:\t"
struct info_process {
char name[60];
char state[2];
int uid;
int ppid;
};
struct info_user {
char name[40];
int uid;
};
struct info_user_list {
int count_max;
int count_current;
struct info_user * list;
};
int read_file(FILE * fd, char * buffer, int buffer_len) {
int read = 0;
while (!feof(fd) && !ferror(fd) && read < buffer_len - 1)
read += fread(buffer+read, 1, 1024, fd);
buffer[read] = 0;
return read;
}
void parse_status(char * buffer, struct info_process * info) {
char * str;
str = strtok(buffer, "\n");
memset(info, 0, sizeof(*info));
while (str != NULL) {
if ( strncmp(str, NAME, strlen(NAME)) == 0 )
strncpy(info->name, str+strlen(NAME), sizeof(info->name)-1);
if ( strncmp(str, STATE, strlen(STATE)) == 0 ) {
strncpy(info->state, str+strlen(STATE), 1); // want only 1 char
}
if ( strncmp(str, UID, strlen(UID)) == 0 )
info->uid = atoi(str+strlen(UID));
if ( strncmp(str, PPID, strlen(PPID)) == 0 )
info->ppid = atoi(str+strlen(PPID));
str = strtok(NULL, "\n");
}
}
void add_user_to_list(struct info_user_list * user_list, char * username, int uid) {
if(user_list->count_max <= user_list->count_current) {
user_list->list = realloc(user_list->list, sizeof(struct info_user) * (user_list->count_current+3));
user_list->count_max = user_list->count_current+3;
}
memset(&user_list->list[user_list->count_current], 0, sizeof(struct info_user));
strncpy(user_list->list[user_list->count_current].name, username, sizeof(user_list->list[user_list->count_current].name) - 1);
user_list->list[user_list->count_current].uid = uid;
user_list->count_current++;
return;
}
// parse password to get username/uid association
int parse_passwd_file(char * passwd_filename, struct info_user_list ** user_list) {
FILE * fd;
struct info_user_list * tmp;
char buffer[2048];
char tmp_username[40];
char * str;
int tmp_uid;
fd = fopen(passwd_filename, "r");
if (!fd)
return -1;
tmp = (struct info_user_list *)malloc(sizeof(struct info_user_list));
if (!tmp)
return -1;
// allocate some space for 10 users
tmp->list = (struct info_user *)malloc(sizeof(struct info_user) * 10);
if (!tmp->list) {
free(tmp);
return -1;
}
tmp->count_max = 10;
tmp->count_current = 0;
while(!feof(fd) && !ferror(fd)) {
// read 1 line at a time
if (fgets(buffer, sizeof(buffer)-1, fd) != NULL) {
str = strtok(buffer, ":");
if (str) {
// first member before : is username
memset(tmp_username, 0, sizeof(tmp_username));
strncpy(tmp_username, str, sizeof(tmp_username)-1);
str = strtok(NULL, ":");
if(!str) // bad line, doesn't contain more than 1 time ":"
continue;
str = strtok(NULL, ":");
if(!str) // bad line, doesn't contain more than 2 times ":"
continue;
// str points to uid
tmp_uid = atoi(str);
add_user_to_list(tmp, tmp_username, tmp_uid);
}
}
}
*user_list = tmp;
fclose(fd);
return 0;
}
void uid_to_username(struct info_user_list * user_list, int uid, char * username, int username_buffer_length) {
int i;
memset(username, 0, username_buffer_length);
for (i=0; i<user_list->count_current; i++) {
if (user_list->list[i].uid == uid) {
strncpy(username, user_list->list[i].name, username_buffer_length-1);
return;
}
}
// fallback if we didn't find the username
snprintf(username, username_buffer_length-1, "%d", uid);
return ;
}
/*
* Generate a process list by parsing /proc/<pid> directories
* linux specific
*/
DWORD ps_list_linux( Packet * response )
{
DWORD result = ERROR_NOT_SUPPORTED;
DIR * dir;
int i;
int read;
struct dirent * entry;
struct stat stat_buf;
char is_process_dir;
char file_path[50];
char file_buffer[2048];
char username[40];
char process_name[50];
char * cmdline;
FILE * fd;
struct info_process info;
struct info_user_list * user_list;
user_list = NULL;
parse_passwd_file("/etc/passwd", &user_list);
dir = opendir("/proc");
if(dir) {
while( (entry=readdir(dir) ) != NULL) {
memset(file_path, 0, sizeof(file_path));
snprintf(file_path, sizeof(file_path)-1, "/proc/%s", entry->d_name);
// handle only directories
if ( (stat(file_path, &stat_buf) != 0) || !(S_ISDIR(stat_buf.st_mode)) )
continue;
// assume it's a dir name
is_process_dir = 1;
// check if the directory name is a process dir name (ie. only digits)
for (i=0; i<strlen(entry->d_name); i++) {
if (!isdigit(entry->d_name[i])) {
is_process_dir = 0;
break;
}
}
if (is_process_dir) {
memset(file_path, 0, sizeof(file_path));
snprintf(file_path, sizeof(file_path)-1, "/proc/%s/status", entry->d_name);
fd = fopen(file_path, "rb");
if (!fd)
continue;
memset(file_buffer, 0, sizeof(file_buffer));
read = read_file(fd, file_buffer, sizeof(file_buffer));
parse_status(file_buffer, &info);
fclose(fd);
/* at the moment, lack support for getpwuid in bionic (defined in stub.c but excluded from compilation)
* so use custom code to parse /etc/passwd
* see parse_passwd_file above
*/
memset(username, 0, sizeof(username));
if (user_list)
uid_to_username(user_list, info.uid, username, sizeof(username));
else
snprintf(username, sizeof(username)-1, "%d", info.uid);
memset(file_path, 0, sizeof(file_path));
snprintf(file_path, sizeof(file_path)-1, "/proc/%s/cmdline", entry->d_name);
fd = fopen(file_path, "rb");
if (!fd)
continue;
memset(process_name, 0, sizeof(process_name));
memset(file_buffer, 0, sizeof(file_buffer));
read = read_file(fd, file_buffer, sizeof(file_buffer));
fclose(fd);
if (info.state[0] == 'Z') { // zombie
snprintf(process_name, sizeof(process_name)-1, "[%s] <defunct>", info.name);
cmdline = NULL; // no cmdline for zombie process
}
else if (read == 0) { // kernel process
snprintf(process_name, sizeof(process_name)-1, "[%s]", info.name);
cmdline = NULL; // no cmdline for kernel process
}
else {
snprintf(process_name, sizeof(process_name)-1, "%s", info.name);
cmdline = file_buffer; // file_buffer contains the cmdline
for(i=0; i<read; i++)
if(file_buffer[i] == '\0')
file_buffer[i] = ' ';
}
// don't care about process arch
ps_addresult(response, atoi(entry->d_name), info.ppid, process_name, cmdline, username, 0);
// at least 1 process found, return ERROR_SUCCESS;
result = ERROR_SUCCESS;
} // end is_process_dir
} // end while readdir
closedir(dir);
}
if (user_list) {
if(user_list->list)
free(user_list->list);
free(user_list);
}
return result;
}
#endif
/*
* Add the details of a process to the response.
*/
VOID ps_addresult( Packet * response, DWORD dwPid, DWORD dwParentPid, char * cpExeName, char * cpExePath, char * cpUserName, DWORD dwProcessArch )
{
Tlv entries[7] = {0};
DWORD dwSessionId = 0;
do
{
if( !response )
break;
#ifdef _WIN32
dwSessionId = session_id( dwPid );
#else
dwSessionId = 0;
#endif
dwPid = htonl( dwPid );
entries[0].header.type = TLV_TYPE_PID;
entries[0].header.length = sizeof( DWORD );
entries[0].buffer = (PUCHAR)&dwPid;
if( !cpExeName )
cpExeName = "";
entries[1].header.type = TLV_TYPE_PROCESS_NAME;
entries[1].header.length = (DWORD)strlen( cpExeName ) + 1;
entries[1].buffer = cpExeName;
if( !cpExePath )
cpExePath = "";
entries[2].header.type = TLV_TYPE_PROCESS_PATH;
entries[2].header.length = (DWORD)strlen( cpExePath ) + 1;
entries[2].buffer = cpExePath;
if( !cpUserName )
cpUserName = "";
entries[3].header.type = TLV_TYPE_USER_NAME;
entries[3].header.length = (DWORD)strlen( cpUserName ) + 1;
entries[3].buffer = cpUserName;
dwProcessArch = htonl( dwProcessArch );
entries[4].header.type = TLV_TYPE_PROCESS_ARCH;
entries[4].header.length = sizeof( DWORD );
entries[4].buffer = (PUCHAR)&dwProcessArch;
dwParentPid = htonl( dwParentPid );
entries[5].header.type = TLV_TYPE_PARENT_PID;
entries[5].header.length = sizeof( DWORD );
entries[5].buffer = (PUCHAR)&dwParentPid;
dwSessionId = htonl( dwSessionId );
entries[6].header.type = TLV_TYPE_PROCESS_SESSION;
entries[6].header.length = sizeof( DWORD );
entries[6].buffer = (PUCHAR)&dwSessionId;
packet_add_tlv_group( response, TLV_TYPE_PROCESS_GROUP, entries, 7 );
} while(0);
}

View File

@ -1,9 +1,12 @@
//===============================================================================================//
#ifndef _METERPRETER_SOURCE_EXTENSION_STDAPI_STDAPI_SERVER_PROCESS_PS_H
#ifndef _METERPRETER_SOURCE_EXTENSION_STDAPI_STDAPI_SERVER_PROCESS_PS_H
#define _METERPRETER_SOURCE_EXTENSION_STDAPI_STDAPI_SERVER_PROCESS_PS_H
//===============================================================================================//
#ifdef _WIN32
VOID ps_addresult( Packet * response, DWORD dwPid, DWORD dwParentPid, char * cpExeName, char * cpExePath, char * cpUserName, DWORD dwProcessArch );
#ifdef _WIN32
typedef DWORD (WINAPI * GETMODULEFILENAMEEXA)( HANDLE hProcess, HMODULE hModule, LPTSTR lpExeName, DWORD dwSize );
typedef DWORD (WINAPI * GETPROCESSIMAGEFILENAMEA)( HANDLE hProcess, LPTSTR lpExeName, DWORD dwSize );
@ -77,7 +80,11 @@ DWORD ps_list_via_psapi( Packet * response );
DWORD ps_list_via_brute( Packet * response );
//===============================================================================================//
#else // linux
DWORD ps_list_linux( Packet * response );
#endif // _WIN32
#endif
//===============================================================================================//
//===============================================================================================//

View File

@ -39,6 +39,7 @@ objects = \
server/sys/config/config.o \
server/sys/process/linux-in-mem-exe.o \
server/sys/process/process.o \
server/sys/process/ps.o \
all: ext_server_stdapi.so

View File

@ -229,31 +229,50 @@ class Console::CommandDispatcher::Stdapi::Sys
#
def cmd_ps(*args)
processes = client.sys.process.get_processes
tbl = Rex::Ui::Text::Table.new(
'Header' => "Process list",
'Indent' => 1,
'Columns' =>
[
"PID",
"Name",
"Arch",
"Session",
"User",
"Path"
])
if (client.platform =~ /win/i)
tbl = Rex::Ui::Text::Table.new(
'Header' => "Process list",
'Indent' => 1,
'Columns' =>
[
"PID",
"Name",
"Arch",
"Session",
"User",
"Path"
])
processes.each { |ent|
processes.each { |ent|
session = ent['session'] == 0xFFFFFFFF ? '' : ent['session'].to_s
arch = ent['arch']
session = ent['session'] == 0xFFFFFFFF ? '' : ent['session'].to_s
arch = ent['arch']
# for display and consistency with payload naming we switch the internal 'x86_64' value to display 'x64'
if( arch == ARCH_X86_64 )
arch = "x64"
end
# for display and consistency with payload naming we switch the internal 'x86_64' value to display 'x64'
if( arch == ARCH_X86_64 )
arch = "x64"
end
tbl << [ ent['pid'].to_s, ent['name'], arch, session, ent['user'], ent['path'] ]
}
tbl << [ ent['pid'].to_s, ent['name'], arch, session, ent['user'], ent['path'] ]
}
else # linux meterpreter
tbl = Rex::Ui::Text::Table.new(
'Header' => "Process list",
'Indent' => 1,
'Columns' =>
[
"PID",
"PPID",
"Name",
"User ID",
"Path"
])
processes.each { |ent|
# can have some unicode characters when receiving name, path.. so need to unicode_filter_decode them
# otherelse, print "$U$/usr/lib/xfce4cpugraphplugin/.." instead of "/usr/lib/xfce4-cpugraph-plugin/.."
tbl << [ ent['pid'].to_s, ent['parentid'].to_s, client.unicode_filter_decode(ent['name']), client.unicode_filter_decode(ent['user']), client.unicode_filter_decode(ent['path']) ]
}
end
if (processes.length == 0)
print_line("No running processes were found.")