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
parent
6bbf018423
commit
5d7190e8cb
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
//===============================================================================================//
|
||||
//===============================================================================================//
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.")
|
||||
|
|
Loading…
Reference in New Issue