Dynamic Executables via .NET with Hostess' DotNetAvBypass #701

Closed
wants to merge 10 commits into
from

Conversation

Projects
None yet
3 participants
@sempervictus
Contributor

sempervictus commented Aug 18, 2012

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.

RageLtMan added some commits Aug 14, 2012

RageLtMan
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.
RageLtMan
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)
RageLtMan
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
RageLtMan
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
RageLtMan
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.
RageLtMan
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
+ return if dot_net_code.nil? or dot_net_code.empty?
+
+ # Ensure we're not running ASCII-8bit through powershell
+ dot_net_code = dot_net_code.force_encoding('ASCII')

This comment has been minimized.

@jlee-r7

jlee-r7 Aug 20, 2012

Contributor

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

@jlee-r7

jlee-r7 Aug 20, 2012

Contributor

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

@@ -1120,12 +1121,12 @@ def self.to_win32pe_psh_net(framework, code, opts={})
end
psh << lines.join("") + "\r\n\r\n"
- 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"
+ 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"

This comment has been minimized.

@jlee-r7

jlee-r7 Aug 20, 2012

Contributor

Ridiculously long line. 100 columns, please

@jlee-r7

jlee-r7 Aug 20, 2012

Contributor

Ridiculously long line. 100 columns, please

+ return 0
+ end
+ # Havent figured this one out yet, but we need a PID owned by a user, cant steal tokens either
+ if client.sys.config.getuid == 'NT AUTHORITY\SYSTEM'

This comment has been minimized.

@jlee-r7

jlee-r7 Aug 20, 2012

Contributor

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

@jlee-r7

jlee-r7 Aug 20, 2012

Contributor

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

+
+ com_opts = {}
+ com_opts[:net_clr] = '4.0'.to_f # Min .NET runtime to load into a PS session
+ com_opts[:target] = datastore['OUTPUT_TARGET'] || session.fs.file.expand_path('%TEMP%') + "\\#{ Rex::Text.rand_text_alpha(rand(8)+8) }.exe"

This comment has been minimized.

@jlee-r7

jlee-r7 Aug 20, 2012

Contributor

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
@jlee-r7

jlee-r7 Aug 20, 2012

Contributor

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
+ com_opts[:target] = datastore['OUTPUT_TARGET'] || session.fs.file.expand_path('%TEMP%') + "\\#{ Rex::Text.rand_text_alpha(rand(8)+8) }.exe"
+ com_opts[:payload] = payload_script
+ if datastore['SVC_GEN']
+ com_opts[:harness] = File.join(Msf::Config.install_root, 'external', 'source', 'psh_exe', 'dot_net_service.cs')

This comment has been minimized.

@jlee-r7

jlee-r7 Aug 20, 2012

Contributor

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

@jlee-r7

jlee-r7 Aug 20, 2012

Contributor

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

+ end
+
+
+ # This should be handled by the exploit mixin, right?

This comment has been minimized.

@jlee-r7

jlee-r7 Aug 20, 2012

Contributor

Yes, you definitely should not be creating payloads yourself.

@jlee-r7

jlee-r7 Aug 20, 2012

Contributor

Yes, you definitely should not be creating payloads yourself.

This comment has been minimized.

@sempervictus

sempervictus Aug 20, 2012

Contributor

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?

@sempervictus

sempervictus Aug 20, 2012

Contributor

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?

+ 'Description' => %q{
+ This module will build a .NET source file using powershell. The compiler builds
+ the executable or library in memory and produces a binary. After compilation the
+ PoweShell session can also sign the executable if provided a path the a .pfx formatted

This comment has been minimized.

@jlee-r7

jlee-r7 Aug 20, 2012

Contributor

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

@jlee-r7

jlee-r7 Aug 20, 2012

Contributor

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

+ register_options(
+ [
+ OptPath.new('SOURCE_FILE', [true, 'Path to source code']),
+ OptBool.new('RUN_BINARY', [false, 'Execute the genrated binary', false]),

This comment has been minimized.

@jlee-r7

jlee-r7 Aug 20, 2012

Contributor

s/genrated/generated/

@jlee-r7

jlee-r7 Aug 20, 2012

Contributor

s/genrated/generated/

+ return 0
+ end
+ # Havent figured this one out yet, but we need a PID owned by a user, cant steal tokens either
+ if client.sys.config.getuid == 'NT AUTHORITY\SYSTEM'

This comment has been minimized.

@jlee-r7

jlee-r7 Aug 20, 2012

Contributor

Localized, see above.

@jlee-r7

jlee-r7 Aug 20, 2012

Contributor

Localized, see above.

This comment has been minimized.

@sempervictus

sempervictus Aug 20, 2012

Contributor

Whats the non-localized way to get privilege level?

@sempervictus

sempervictus Aug 20, 2012

Contributor

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