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://stackoverflow.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://stackoverflow.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://stackoverflow.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://stackoverflow.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