PSExec允许用户连接到远程计算机并通过命名管道执行命令。命名管道是通过一个随机命名的二进制文件建立的,该文件被写入远程计算机上的ADMIN $共享,并被SVCManager用来创建新服务。 您可以想象此步骤正在运行:sc create [serviceName] binPath= "C:\Windows\[uploaded-binary].exe"。 建立命名管道后,您与远程计算机之间的所有命令输入和输出都通过SMB协议(445 / TCP)进行通信。
如果未开启共享 需要开启注册表的方式开启共享
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters AutoShareWks && AutoShareServer 修改为 1 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa restrictanonymous 修改为 1
我们拿一个普通web用户登录看看
可以看见日志4624后又紧跟着4634直接注销了,注销是因为web用户没有访问Admin$共享的权限
有关NTML的知识如果不熟悉的同学可以看
https://www.anquanke.com/post/id/210323
由此可以见普通的用户是没有访问admin$共享的权限的
我们接下来用Administrator尝试,查看数据包结果总结出来的流程
1192.168.1.102用192.168.1.100administartor进行NTML认证成功 2建立IPC$共享成功 3连接admin$共享 4向192.168.1.100admin$共享写入PE文件 5创建启动此文件的服务 6启动服务 7删除服务 8删除文件
1这是向admin$共享写入文件的步骤
我们需要知道的是传输文件这整个过程 与其交互的仅仅是445端口
但是我们发现这里为什么使用了DCE/RPC和EPM走了135端口,那么必须一定要走一次135吗?这里调用DCE/RPC和EPM的作用又是什么?我们带着问题继续思考
可以知道DCE/RPC协议是基于MSRPC协议的,而DCERPC并没有创建服务,只是一个远程调用协议。
https://*.com/questions/51346269/understanding-smb-and-dcerpc-for-remote-command-execution-capabilities
那么EPM协议又是什么呐?
https://wiki.wireshark.org/EPM https://help.stonesoft.com/onlinehelp/StoneGate/SMC/6.3.0/GUID-1DDB90C6-0BC1-45C1-8ED8-F56A98513F28.html
我们接下来关闭135端口看能不能psexec成功,可以看见在只有445端口开放的情况下,还是能成功psexec,我们继续抓包看看和之前135开放的包有什么区别
可以看见这里的DCERPC协议的作用和开放135端口情况下的DCERPC协议的作用是不相同的,
然后紧接着安装psexesvc服务
这里又创建了一些列的命名管道
首先,我们需要清楚的是,命名管道基于smb协议,smb,smb而不是tcp进行通信。重要的事情说了三遍。它用于在两个进程之间进行通信。这两个进程可以是本地进程或远程进程。命名管道有点类似于套接字连接。它们用于传输数据。可以设置特定的权限,以便具有指定权限的进程可以连接到命名管道。从理论上讲,每个程序都可以连接到命名管道,但是它们在连接后可以做不同的事情。可以完成的操作取决于服务器的配置。 以下总结了几点: 1.命名管道是C / S体系结构,服务器上的进程必须首先创建一个命名管道。 2.可以通过满足权限的任何进程访问命名管道,并且可以自定义可以访问哪些权限。 3.客户端可以是本地进程,也可以是远程进程。本地进程访问\。\ pipe \ pipename中的命名管道,而远程进程访问\ ip \ pipe \ pipename。
上面的psexesvc管道是用于服务本身的,但是下面这三个管道则不是
更具以上分析搞了一个自己的psexec demo
Mypsexec.cs
using System; using System.Diagnostics; using System.IO; using System.IO.Pipes; using System.Text; //All the above are namespaces, similar to import in python namespace Server //Namespace declaration { class Server { static void Main(string[] args) //Program entry point { /**When you are not sure what type of variable is, use var. The NamedPipeServerStream class is Under the System.IO.Pipes namespace. Using it in the way of using is equivalent to with in python Like opening a file, to a certain extent, to prevent forgetting to release some resources, you can also avoid using using. **/ using (var pipe = new NamedPipeServerStream( "Mypsexec", PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Message)) /**Set the pipe name to psexecsvc, the pipe communication mode is two-way communication, both parties can send information You can also receive information. The maximum number of connections is the default maximum number of connections. The last parameter represents Use information flow to transfer data, not byte stream, remember to use information flow, because whenever Using byte streams, the other party may not be able to receive all the information that has been sent, and the information stream can be guaranteed All data sent by the certificate can be received. **/ { Console.WriteLine("[*] Waiting for client connection..."); pipe.WaitForConnection();//Waiting for the connection from the other end of the pipeline Console.WriteLine("[*] Client connected."); while (true) { /** Pass the byte type array received from the named pipe to messageBytes, this word The section array is the binary form of the data sent by the client. **/ var messageBytes = ReadMessage(pipe); //Store the string generated by UTF-8 decoding of the byte type array into the line var line = Encoding.UTF8.GetString(messageBytes); Console.WriteLine("[*] Received: {0}", line); //Convert the received string to consumer agreement, if the content is exit, exit the program. if (line.ToLower() == "exit") return; /** Create a ProcessStartInfo class, this class is used to specify the relevant attributes of a process. **/ var processStartInfo = new ProcessStartInfo { //Start cmd FileName = "cmd.exe", //The parameter is /c + line, line is the data received from the named pipe Arguments = "/c " + line, //From the standard output of Dingxi RedirectStandardOutput = true, //Redirect standard error output RedirectStandardError = true, //By setting this attribute to false, standard input, output and error streams can be redirected. UseShellExecute = false }; try { /** Start the process with the information previously defined, and jump to the catch block if an error occurs. What returned is A process class, my understanding is that this process class is a program handle, you can Allows you to perform specified operations on the program, such as opening and closing. **/ var process = Process.Start(processStartInfo); /** Read all the standard output of the process, and combine the standard error output and standard output into one Strings. **/ var output = process.StandardOutput.ReadToEnd(); output += process.StandardError.ReadToEnd(); //Waiting for the end of the thread can be understood as waiting for the end of the above command process.WaitForExit(); //If output is equal to empty or null, assign a newline character to it. if (string.IsNullOrEmpty(output)) { output = "\n"; } //Encode the output as a byte array in UTF. var response = Encoding.UTF8.GetBytes(output); //Write all the data of this byte array to the pipe in the named pipe. pipe.Write(response, 0, response.Length); } catch (Exception ex) { /**If a line of code in the try block runs incorrectly, catch the error. This error is Represented by string type, convert this error into a byte array and output to the named pipe In. **/ Console.WriteLine(ex); var response = Encoding.UTF8.GetBytes(ex.Message); pipe.Write(response, 0, response.Length); } } } } private static byte[] ReadMessage(PipeStream pipe) { byte[] buffer = new byte[1024];//Create an array that can store 1024 bytes of data //Create a memory stream class for data transfer using (var ms = new MemoryStream()) { do { /**Read data from the named pipe, read byte blocks from 0, read at most buffer.Length is 1024, and then the number of bytes read out is returned to redBytes, write the read data to the buffer. **/ var readBytes = pipe.Read(buffer, 0, buffer.Length); /** Read data from 0 byte from the buffer, read the redBytes byte, and then Then write these data to the current memory stream. **/ ms.Write(buffer, 0, readBytes); } //If the information in the named pipe has not been read, it will always perform the read operation. while (!pipe.IsMessageComplete); return ms.ToArray(); /** Write the data in the memory stream to the array and return a Byte class Type array. **/ } } } }
psexec.cs
/*using System; using System.Runtime.InteropServices; using System.Threading; using System.IO; using System.IO.Pipes; using System.Text; namespace ExtremeMirror { public class PinvokeWindowsNetworking { static void Main(string[] args) { connectToRemote("\\\\192.168.1.100", "localhost", "123456789789"); //Connect to the named pipe on the local computer, the mode of bidirectional data transmission, the pipe name is psexecsvc using (var pipe = new NamedPipeClientStream("192.168.1.100", "psexecsvc", PipeDirection.InOut)) { //Connect to the named pipe, the supermarket time is 5000 milliseconds pipe.Connect(5000); //Set the data reading method to message pipe.ReadMode = PipeTransmissionMode.Message; do { Console.Write("MyPsexec> "); //Receive data from the command line var input = Console.ReadLine(); //If the received data is empty or null, jump out of this loop if (String.IsNullOrEmpty(input)) continue; //Convert the output string to byte array type and store byte[] bytes = Encoding.Default.GetBytes(input); //Write the converted data to the named pipe pipe.Write(bytes, 0, bytes.Length); //Change the conceit of the output to lowercase and then determine whether it is equal to exit, if it is, exit the program if (input.ToLower() == "exit") return; //Read data from the named pipe var result = ReadMessage(pipe); //Output Data Console.WriteLine(Encoding.UTF8.GetString(result)); Console.WriteLine(); } while (true); } } private static byte[] ReadMessage(PipeStream pipe) { byte[] buffer = new byte[1024]; using (var ms = new MemoryStream()) { do { var readBytes = pipe.Read(buffer, 0, buffer.Length); ms.Write(buffer, 0, readBytes); } while (!pipe.IsMessageComplete); return ms.ToArray(); } } [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)] static extern IntPtr CreateService( IntPtr hSCManager, string lpServiceName, string lpDisplayName, uint dwDesiredAccess, uint dwServiceType, uint dwStartType, uint dwErrorControl, string lpBinaryPathName, string lpLoadOrderGroup, string lpdwTagId, string lpDependencies, string lpServiceStartName, string lpPassword); #region Consts const int RESOURCE_CONNECTED = 0x00000001; const int RESOURCE_GLOBALNET = 0x00000002; const int RESOURCE_REMEMBERED = 0x00000003; const int RESOURCETYPE_ANY = 0x00000000; const int RESOURCETYPE_DISK = 0x00000001; const int RESOURCETYPE_PRINT = 0x00000002; const int RESOURCEDISPLAYTYPE_GENERIC = 0x00000000; const int RESOURCEDISPLAYTYPE_DOMAIN = 0x00000001; const int RESOURCEDISPLAYTYPE_SERVER = 0x00000002; const int RESOURCEDISPLAYTYPE_SHARE = 0x00000003; const int RESOURCEDISPLAYTYPE_FILE = 0x00000004; const int RESOURCEDISPLAYTYPE_GROUP = 0x00000005; const int RESOURCEUSAGE_CONNECTABLE = 0x00000001; const int RESOURCEUSAGE_CONTAINER = 0x00000002; const int CONNECT_INTERACTIVE = 0x00000008; const int CONNECT_PROMPT = 0x00000010; const int CONNECT_REDIRECT = 0x00000080; const int CONNECT_UPDATE_PROFILE = 0x00000001; const int CONNECT_COMMANDLINE = 0x00000800; const int CONNECT_CMD_SAVECRED = 0x00001000; const int CONNECT_LOCALDRIVE = 0x00000100; #endregion #region Errors const int NO_ERROR = 0; const int ERROR_ACCESS_DENIED = 5; const int ERROR_ALREADY_ASSIGNED = 85; const int ERROR_BAD_DEVICE = 1200; const int ERROR_BAD_NET_NAME = 67; const int ERROR_BAD_PROVIDER = 1204; const int ERROR_CANCELLED = 1223; const int ERROR_EXTENDED_ERROR = 1208; const int ERROR_INVALID_ADDRESS = 487; const int ERROR_INVALID_PARAMETER = 87; const int ERROR_INVALID_PASSWORD = 1216; const int ERROR_MORE_DATA = 234; const int ERROR_NO_MORE_ITEMS = 259; const int ERROR_NO_NET_OR_BAD_PATH = 1203; const int ERROR_NO_NETWORK = 1222; const int ERROR_BAD_PROFILE = 1206; const int ERROR_CANNOT_OPEN_PROFILE = 1205; const int ERROR_DEVICE_IN_USE = 2404; const int ERROR_NOT_CONNECTED = 2250; const int ERROR_OPEN_FILES = 2401; private struct ErrorClass { public int num; public string message; public ErrorClass(int num, string message) { this.num = num; this.message = message; } } // Created with excel formula: // ="new ErrorClass("&A1&", """&PROPER(SUBSTITUTE(MID(A1,7,LEN(A1)-6), "_", " "))&"""), " private static ErrorClass[] ERROR_LIST = new ErrorClass[] { new ErrorClass(ERROR_ACCESS_DENIED, "Error: Access Denied"), new ErrorClass(ERROR_ALREADY_ASSIGNED, "Error: Already Assigned"), new ErrorClass(ERROR_BAD_DEVICE, "Error: Bad Device"), new ErrorClass(ERROR_BAD_NET_NAME, "Error: Bad Net Name"), new ErrorClass(ERROR_BAD_PROVIDER, "Error: Bad Provider"), new ErrorClass(ERROR_CANCELLED, "Error: Cancelled"), new ErrorClass(ERROR_EXTENDED_ERROR, "Error: Extended Error"), new ErrorClass(ERROR_INVALID_ADDRESS, "Error: Invalid Address"), new ErrorClass(ERROR_INVALID_PARAMETER, "Error: Invalid Parameter"), new ErrorClass(ERROR_INVALID_PASSWORD, "Error: Invalid Password"), new ErrorClass(ERROR_MORE_DATA, "Error: More Data"), new ErrorClass(ERROR_NO_MORE_ITEMS, "Error: No More Items"), new ErrorClass(ERROR_NO_NET_OR_BAD_PATH, "Error: No Net Or Bad Path"), new ErrorClass(ERROR_NO_NETWORK, "Error: No Network"), new ErrorClass(ERROR_BAD_PROFILE, "Error: Bad Profile"), new ErrorClass(ERROR_CANNOT_OPEN_PROFILE, "Error: Cannot Open Profile"), new ErrorClass(ERROR_DEVICE_IN_USE, "Error: Device In Use"), new ErrorClass(ERROR_EXTENDED_ERROR, "Error: Extended Error"), new ErrorClass(ERROR_NOT_CONNECTED, "Error: Not Connected"), new ErrorClass(ERROR_OPEN_FILES, "Error: Open Files"), }; private static string getErrorForNumber(int errNum) { foreach (ErrorClass er in ERROR_LIST) { if (er.num == errNum) return er.message; } return "Error: Unknown, " + errNum; } #endregion [DllImport("Mpr.dll")] private static extern int WNetUseConnection( IntPtr hwndOwner, NETRESOURCE lpNetResource, string lpPassword, string lpUserID, int dwFlags, string lpAccessName, string lpBufferSize, string lpResult ); [DllImport("Mpr.dll")] private static extern int WNetCancelConnection2( string lpName, int dwFlags, bool fForce ); [StructLayout(LayoutKind.Sequential)] private class NETRESOURCE { public int dwScope = 0; public int dwType = 0; public int dwDisplayType = 0; public int dwUsage = 0; public string lpLocalName = ""; public string lpRemoteName = ""; public string lpComment = ""; public string lpProvider = ""; } public static string connectToRemote(string remoteUNC, string username, string password) { return connectToRemote(remoteUNC, username, password, false); } public static string connectToRemote(string remoteUNC, string username, string password, bool promptUser) { NETRESOURCE nr = new NETRESOURCE(); nr.dwType = RESOURCETYPE_DISK; nr.lpRemoteName = remoteUNC; // nr.lpLocalName = "F:"; int ret; if (promptUser) ret = WNetUseConnection(IntPtr.Zero, nr, "", "", CONNECT_INTERACTIVE | CONNECT_PROMPT, null, null, null); else ret = WNetUseConnection(IntPtr.Zero, nr, password, username, 0, null, null, null); if (ret == NO_ERROR) return null; return getErrorForNumber(ret); } public static string disconnectRemote(string remoteUNC) { int ret = WNetCancelConnection2(remoteUNC, CONNECT_UPDATE_PROFILE, false); if (ret == NO_ERROR) return null; return getErrorForNumber(ret); } } }*/ using System; using System.Runtime.InteropServices; using System.Threading; /// <summary> /// <para> /// Sources: /// <para>https://*.com/questions/358700/how-to-install-a-windows-service-programmatically-in-c </para> /// <para>https://www.c-sharpcorner.com/article/create-windows-services-in-c-sharp/</para> /// </para> /// /// <para> /// Installs and starts the service /// ServiceInstaller.InstallAndStart("MyServiceName", "MyServiceDisplayName", "C:\\PathToServiceFile.exe"); /// </para> /// <para> /// Removes the service /// ServiceInstaller.Uninstall("MyServiceName"); /// </para> /// <para> /// Checks the status of the service /// ServiceInstaller.GetServiceStatus("MyServiceName"); /// </para> /// <para> /// Starts the service /// ServiceInstaller.StartService("MyServiceName"); /// </para> /// <para> /// Stops the service /// ServiceInstaller.StopService("MyServiceName"); /// </para> /// <para> /// Check if service is installed /// ServiceInstaller.ServiceIsInstalled("MyServiceName"); /// </para> /// </summary> using System.Runtime.InteropServices; using System.Threading; using System.IO; using System.IO.Pipes; using System.Text; public static class ServiceInstaller { static void Main(string[] args) { connectToRemote("\\\\192.168.1.100", "system\\administrator", "123456"); Console.WriteLine("连接成功"); File.Copy(@"Mypsexec.exe", "\\\\192.168.1.100\\c$\\programdata\\Mypsexec.exe"); Install("Dce", "Dce", "c:\\programdata\\Mypsexec.exe"); StartService("Dce"); //Connect to the named pipe on the local computer, the mode of bidirectional data transmission, the pipe name is psexecsvc using (var pipe = new NamedPipeClientStream("192.168.1.100", "Mypsexec", PipeDirection.InOut)) { //Connect to the named pipe, the supermarket time is 5000 milliseconds pipe.Connect(5000); //Set the data reading method to message pipe.ReadMode = PipeTransmissionMode.Message; do { Console.Write("MyPsexecCMd> "); //Receive data from the command line var input = Console.ReadLine(); //If the received data is empty or null, jump out of this loop if (String.IsNullOrEmpty(input)) continue; //Convert the output string to byte array type and store byte[] bytes = Encoding.Default.GetBytes(input); //Write the converted data to the named pipe pipe.Write(bytes, 0, bytes.Length); //Change the conceit of the output to lowercase and then determine whether it is equal to exit, if it is, exit the program if (input.ToLower() == "exit") return; //Read data from the named pipe var result = ReadMessage(pipe); //Output Data Console.WriteLine(Encoding.UTF8.GetString(result)); Console.WriteLine(); } while (true); } } private static byte[] ReadMessage(PipeStream pipe) { byte[] buffer = new byte[1024]; using (var ms = new MemoryStream()) { do { var readBytes = pipe.Read(buffer, 0, buffer.Length); ms.Write(buffer, 0, readBytes); } while (!pipe.IsMessageComplete); return ms.ToArray(); } } [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)] static extern IntPtr CreateService( IntPtr hSCManager, string lpServiceName, string lpDisplayName, uint dwDesiredAccess, uint dwServiceType, uint dwStartType, uint dwErrorControl, string lpBinaryPathName, string lpLoadOrderGroup, string lpdwTagId, string lpDependencies, string lpServiceStartName, string lpPassword); #region Consts const int RESOURCE_CONNECTED = 0x00000001; const int RESOURCE_GLOBALNET = 0x00000002; const int RESOURCE_REMEMBERED = 0x00000003; const int RESOURCETYPE_ANY = 0x00000000; const int RESOURCETYPE_DISK = 0x00000001; const int RESOURCETYPE_PRINT = 0x00000002; const int RESOURCEDISPLAYTYPE_GENERIC = 0x00000000; const int RESOURCEDISPLAYTYPE_DOMAIN = 0x00000001; const int RESOURCEDISPLAYTYPE_SERVER = 0x00000002; const int RESOURCEDISPLAYTYPE_SHARE = 0x00000003; const int RESOURCEDISPLAYTYPE_FILE = 0x00000004; const int RESOURCEDISPLAYTYPE_GROUP = 0x00000005; const int RESOURCEUSAGE_CONNECTABLE = 0x00000001; const int RESOURCEUSAGE_CONTAINER = 0x00000002; const int CONNECT_INTERACTIVE = 0x00000008; const int CONNECT_PROMPT = 0x00000010; const int CONNECT_REDIRECT = 0x00000080; const int CONNECT_UPDATE_PROFILE = 0x00000001; const int CONNECT_COMMANDLINE = 0x00000800; const int CONNECT_CMD_SAVECRED = 0x00001000; const int CONNECT_LOCALDRIVE = 0x00000100; #endregion #region Errors const int NO_ERROR = 0; const int ERROR_ACCESS_DENIED = 5; const int ERROR_ALREADY_ASSIGNED = 85; const int ERROR_BAD_DEVICE = 1200; const int ERROR_BAD_NET_NAME = 67; const int ERROR_BAD_PROVIDER = 1204; const int ERROR_CANCELLED = 1223; const int ERROR_EXTENDED_ERROR = 1208; const int ERROR_INVALID_ADDRESS = 487; const int ERROR_INVALID_PARAMETER = 87; const int ERROR_INVALID_PASSWORD = 1216; const int ERROR_MORE_DATA = 234; const int ERROR_NO_MORE_ITEMS = 259; const int ERROR_NO_NET_OR_BAD_PATH = 1203; const int ERROR_NO_NETWORK = 1222; const int ERROR_BAD_PROFILE = 1206; const int ERROR_CANNOT_OPEN_PROFILE = 1205; const int ERROR_DEVICE_IN_USE = 2404; const int ERROR_NOT_CONNECTED = 2250; const int ERROR_OPEN_FILES = 2401; private struct ErrorClass { public int num; public string message; public ErrorClass(int num, string message) { this.num = num; this.message = message; } } // Created with excel formula: // ="new ErrorClass("&A1&", """&PROPER(SUBSTITUTE(MID(A1,7,LEN(A1)-6), "_", " "))&"""), " private static ErrorClass[] ERROR_LIST = new ErrorClass[] { new ErrorClass(ERROR_ACCESS_DENIED, "Error: Access Denied"), new ErrorClass(ERROR_ALREADY_ASSIGNED, "Error: Already Assigned"), new ErrorClass(ERROR_BAD_DEVICE, "Error: Bad Device"), new ErrorClass(ERROR_BAD_NET_NAME, "Error: Bad Net Name"), new ErrorClass(ERROR_BAD_PROVIDER, "Error: Bad Provider"), new ErrorClass(ERROR_CANCELLED, "Error: Cancelled"), new ErrorClass(ERROR_EXTENDED_ERROR, "Error: Extended Error"), new ErrorClass(ERROR_INVALID_ADDRESS, "Error: Invalid Address"), new ErrorClass(ERROR_INVALID_PARAMETER, "Error: Invalid Parameter"), new ErrorClass(ERROR_INVALID_PASSWORD, "Error: Invalid Password"), new ErrorClass(ERROR_MORE_DATA, "Error: More Data"), new ErrorClass(ERROR_NO_MORE_ITEMS, "Error: No More Items"), new ErrorClass(ERROR_NO_NET_OR_BAD_PATH, "Error: No Net Or Bad Path"), new ErrorClass(ERROR_NO_NETWORK, "Error: No Network"), new ErrorClass(ERROR_BAD_PROFILE, "Error: Bad Profile"), new ErrorClass(ERROR_CANNOT_OPEN_PROFILE, "Error: Cannot Open Profile"), new ErrorClass(ERROR_DEVICE_IN_USE, "Error: Device In Use"), new ErrorClass(ERROR_EXTENDED_ERROR, "Error: Extended Error"), new ErrorClass(ERROR_NOT_CONNECTED, "Error: Not Connected"), new ErrorClass(ERROR_OPEN_FILES, "Error: Open Files"), }; private static string getErrorForNumber(int errNum) { foreach (ErrorClass er in ERROR_LIST) { if (er.num == errNum) return er.message; } return "Error: Unknown, " + errNum; } #endregion [DllImport("Mpr.dll")] private static extern int WNetUseConnection( IntPtr hwndOwner, NETRESOURCE lpNetResource, string lpPassword, string lpUserID, int dwFlags, string lpAccessName, string lpBufferSize, string lpResult ); [DllImport("Mpr.dll")] private static extern int WNetCancelConnection2( string lpName, int dwFlags, bool fForce ); [StructLayout(LayoutKind.Sequential)] private class NETRESOURCE { public int dwScope = 0; public int dwType = 0; public int dwDisplayType = 0; public int dwUsage = 0; public string lpLocalName = ""; public string lpRemoteName = ""; public string lpComment = ""; public string lpProvider = ""; } public static string connectToRemote(string remoteUNC, string username, string password) { return connectToRemote(remoteUNC, username, password, false); } public static string connectToRemote(string remoteUNC, string username, string password, bool promptUser) { NETRESOURCE nr = new NETRESOURCE(); nr.dwType = RESOURCETYPE_DISK; nr.lpRemoteName = remoteUNC; // nr.lpLocalName = "F:"; int ret; if (promptUser) ret = WNetUseConnection(IntPtr.Zero, nr, "", "", CONNECT_INTERACTIVE | CONNECT_PROMPT, null, null, null); else ret = WNetUseConnection(IntPtr.Zero, nr, password, username, 0, null, null, null); if (ret == NO_ERROR) return null; return getErrorForNumber(ret); } public static string disconnectRemote(string remoteUNC) { int ret = WNetCancelConnection2(remoteUNC, CONNECT_UPDATE_PROFILE, false); if (ret == NO_ERROR) return null; return getErrorForNumber(ret); } private const int STANDARD_RIGHTS_REQUIRED = 0xF0000; private const int SERVICE_WIN32_OWN_PROCESS = 0x00000010; private const string SERVICES_ACTIVE_DATABASE = null; private class SERVICE_STATUS { public int dwServiceType = 0; public ServiceState dwCurrentState = 0; public int dwControlsAccepted = 0; public int dwWin32ExitCode = 0; public int dwServiceSpecificExitCode = 0; public int dwCheckPoint = 0; public int dwWaitHint = 0; } #region OpenSCManagerW [DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)] static extern IntPtr OpenSCManagerW(string machineName, string databaseName, ScmAccessRights dwDesiredAccess); #endregion #region OpenService [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)] static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, ServiceAccessRights dwDesiredAccess); #endregion #region CreateService [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)] private static extern IntPtr CreateService(IntPtr hSCManager, string lpServiceName, string lpDisplayName, ServiceAccessRights dwDesiredAccess, int dwServiceType, ServiceBootFlag dwStartType, ServiceError dwErrorControl, string lpBinaryPathName, string lpLoadOrderGroup, IntPtr lpdwTagId, string lpDependencies, string lp, string lpPassword); #endregion #region CloseServiceHandle [DllImport("advapi32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] static extern bool CloseServiceHandle(IntPtr hSCObject); #endregion #region QueryServiceStatus [DllImport("advapi32.dll")] private static extern int QueryServiceStatus(IntPtr hService, SERVICE_STATUS lpServiceStatus); #endregion #region DeleteService [DllImport("advapi32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool DeleteService(IntPtr hService); #endregion #region ControlService [DllImport("advapi32.dll")] private static extern int ControlService(IntPtr hService, ServiceControl dwControl, SERVICE_STATUS lpServiceStatus); #endregion #region StartService [DllImport("advapi32.dll", SetLastError = true)] private static extern int StartService(IntPtr hService, int dwNumServiceArgs, int lpServiceArgVectors); #endregion public static void Uninstall(string serviceName) { IntPtr scm = OpenSCManagerW("192.168.1.100", SERVICES_ACTIVE_DATABASE, ScmAccessRights.AllAccess); try { IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.AllAccess); if (service == IntPtr.Zero) throw new ApplicationException("Service not installed."); try { StopService(service); if (!DeleteService(service)) throw new ApplicationException("Could not delete service " + Marshal.GetLastWin32Error()); } finally { CloseServiceHandle(service); } } finally { CloseServiceHandle(scm); } } public static bool ServiceIsInstalled(string serviceName) { IntPtr scm = OpenSCManagerW("192.168.1.100", SERVICES_ACTIVE_DATABASE, ScmAccessRights.AllAccess); try { IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.QueryStatus); if (service == IntPtr.Zero) return false; CloseServiceHandle(service); return true; } finally { CloseServiceHandle(scm); } } public static void InstallAndStart(string serviceName, string displayName, string fileName) { IntPtr scm = OpenSCManagerW("192.168.1.100", SERVICES_ACTIVE_DATABASE, ScmAccessRights.AllAccess); try { IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.AllAccess); if (service == IntPtr.Zero) service = CreateService(scm, serviceName, displayName, ServiceAccessRights.AllAccess, SERVICE_WIN32_OWN_PROCESS, ServiceBootFlag.AutoStart, ServiceError.Normal, fileName, null, IntPtr.Zero, null, null, null); if (service == IntPtr.Zero) throw new ApplicationException("Failed to install service."); try { StartService(service); } finally { CloseServiceHandle(service); } } finally { CloseServiceHandle(scm); } } public static void Install(string serviceName, string displayName, string fileName) { IntPtr scm = OpenSCManagerW("192.168.1.100", SERVICES_ACTIVE_DATABASE, ScmAccessRights.AllAccess); try { IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.AllAccess); if (service == IntPtr.Zero) service = CreateService(scm, serviceName, displayName, ServiceAccessRights.AllAccess, SERVICE_WIN32_OWN_PROCESS, ServiceBootFlag.AutoStart, ServiceError.Ignore, fileName, null, IntPtr.Zero, null, null, null); if (service == IntPtr.Zero) throw new ApplicationException("Failed to install service."); } finally { CloseServiceHandle(scm); } } public static void StartService(string serviceName) { IntPtr scm = OpenSCManagerW("192.168.1.100", SERVICES_ACTIVE_DATABASE,ScmAccessRights.Connect); try { IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.AllAccess); if (service == IntPtr.Zero) throw new ApplicationException("Could not open service."); try { StartService(service); } finally { CloseServiceHandle(service); } } finally { CloseServiceHandle(scm); } } public static void StopService(string serviceName) { IntPtr scm = OpenSCManagerW("192.168.1.100", SERVICES_ACTIVE_DATABASE, ScmAccessRights.AllAccess); try { IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.QueryStatus | ServiceAccessRights.Stop); if (service == IntPtr.Zero) throw new ApplicationException("Could not open service."); try { StopService(service); } finally { CloseServiceHandle(service); } } finally { CloseServiceHandle(scm); } } private static void StartService(IntPtr service) { SERVICE_STATUS status = new SERVICE_STATUS(); StartService(service, 0, 0); var changedStatus = WaitForServiceStatus(service, ServiceState.StartPending, ServiceState.Running); if (!changedStatus) throw new ApplicationException("Unable to start service"); } private static void StopService(IntPtr service) { SERVICE_STATUS status = new SERVICE_STATUS(); ControlService(service, ServiceControl.Stop, status); var changedStatus = WaitForServiceStatus(service, ServiceState.StopPending, ServiceState.Stopped); if (!changedStatus) throw new ApplicationException("Unable to stop service"); } public static ServiceState GetServiceStatus(string serviceName) { IntPtr scm = OpenSCManagerW("192.168.1.100", SERVICES_ACTIVE_DATABASE, ScmAccessRights.AllAccess); try { IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.QueryStatus); if (service == IntPtr.Zero) return ServiceState.NotFound; try { return GetServiceStatus(service); } finally { CloseServiceHandle(service); } } finally { CloseServiceHandle(scm); } } private static ServiceState GetServiceStatus(IntPtr service) { SERVICE_STATUS status = new SERVICE_STATUS(); if (QueryServiceStatus(service, status) == 0) throw new ApplicationException("Failed to query service status."); return status.dwCurrentState; } private static bool WaitForServiceStatus(IntPtr service, ServiceState waitStatus, ServiceState desiredStatus) { SERVICE_STATUS status = new SERVICE_STATUS(); QueryServiceStatus(service, status); if (status.dwCurrentState == desiredStatus) return true; int dwStartTickCount = Environment.TickCount; int dwOldCheckPoint = status.dwCheckPoint; while (status.dwCurrentState == waitStatus) { // Do not wait longer than the wait hint. A good interval is // one tenth the wait hint, but no less than 1 second and no // more than 10 seconds. int dwWaitTime = status.dwWaitHint / 10; if (dwWaitTime < 1000) dwWaitTime = 1000; else if (dwWaitTime > 10000) dwWaitTime = 10000; Thread.Sleep(dwWaitTime); // Check the status again. if (QueryServiceStatus(service, status) == 0) break; if (status.dwCheckPoint > dwOldCheckPoint) { // The service is making progress. dwStartTickCount = Environment.TickCount; dwOldCheckPoint = status.dwCheckPoint; } else { if (Environment.TickCount - dwStartTickCount > status.dwWaitHint) { // No progress made within the wait hint break; } } } return (status.dwCurrentState == desiredStatus); } private static IntPtr OpenSCManagerW(ScmAccessRights rights) { IntPtr scm = OpenSCManagerW(null, null, rights); if (scm == IntPtr.Zero) throw new ApplicationException("Could not connect to service control manager."); return scm; } } public enum ServiceState { Unknown = -1, // The state cannot be (has not been) retrieved. NotFound = 0, // The service is not known on the host server. Stopped = 1, StartPending = 2, StopPending = 3, Running = 4, ContinuePending = 5, PausePending = 6, Paused = 7 } [Flags] public enum ScmAccessRights { Connect = 0x0001, CreateService = 0x0002, EnumerateService = 0x0004, Lock = 0x0008, QueryLockStatus = 0x0010, ModifyBootConfig = 0x0020, StandardRightsRequired = 0xF0000, AllAccess = (StandardRightsRequired | Connect | CreateService | EnumerateService | Lock | QueryLockStatus | ModifyBootConfig) } [Flags] public enum ServiceAccessRights { QueryConfig = 0x1, ChangeConfig = 0x2, QueryStatus = 0x4, EnumerateDependants = 0x8, Start = 0x10, Stop = 0x20, PauseContinue = 0x40, Interrogate = 0x80, UserDefinedControl = 0x100, Delete = 0x00010000, StandardRightsRequired = 0xF0000, AllAccess = (StandardRightsRequired | QueryConfig | ChangeConfig | QueryStatus | EnumerateDependants | Start | Stop | PauseContinue | Interrogate | UserDefinedControl) } public enum ServiceBootFlag { Start = 0x00000000, SystemStart = 0x00000001, AutoStart = 0x00000002, DemandStart = 0x00000003, Disabled = 0x00000004 } public enum ServiceControl { Stop = 0x00000001, Pause = 0x00000002, Continue = 0x00000003, Interrogate = 0x00000004, Shutdown = 0x00000005, ParamChange = 0x00000006, NetBindAdd = 0x00000007, NetBindRemove = 0x00000008, NetBindEnable = 0x00000009, NetBindDisable = 0x0000000A } public enum ServiceError { Ignore = 0x00000000, Normal = 0x00000001, Severe = 0x00000002, Critical = 0x00000003 }
总结
学习完成后总结的Psexec流程 1进行ntml认证建立ipc$连接 2访问admin$文件共享传送文件 3创建服务和管道 4运行服务 5与管道进行交互获取回显 自己的psexec过程 1进行ntml认证建立ipc$ 2传输文件创建服务 3启动服务 4与管道进行交互获取回显 踩坑:1:创建服务1053错误(能创建但是不能启动)
参考
https://www.anquanke.com/post/id/210323 https://www.codenong.com/51346269/ https://*.com/questions/51346269/understanding-smb-and-dcerpc-for-remote-command-execution-capabilities https://blog.f-secure.com/endpoint-detection-of-remote-service-creation-and-psexec/ https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rpce/d35f8e7e-ebb1-43cb-b1f1-c09c716a26f4 https://www.contextis.com/en/blog/lateral-movement-a-deep-look-into-psexec https://www.programmersought.com/article/57217175834/ https://github.com/poweradminllc/PAExec/search?q=1053 https://cpp.hotexamples.com/zh/examples/-/-/CreateService/cpp-createservice-function-examples.html https://docs.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-createservicew https://payloads.online/archivers/2020-04-02/1
renfer
https://www.anquanke.com/post/id/210323 https://www.codenong.com/51346269/ https://*.com/questions/51346269/understanding-smb-and-dcerpc-for-remote-command-execution-capabilities https://blog.f-secure.com/endpoint-detection-of-remote-service-creation-and-psexec/ https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rpce/d35f8e7e-ebb1-43cb-b1f1-c09c716a26f4 https://www.contextis.com/en/blog/lateral-movement-a-deep-look-into-psexec https://www.programmersought.com/article/57217175834/ https://github.com/poweradminllc/PAExec/search?q=1053 https://cpp.hotexamples.com/zh/examples/-/-/CreateService/cpp-createservice-function-examples.html https://docs.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-createservicew https://payloads.online/archivers/2020-04-02/1