CVE-2016-0720 (published 2017-04-21T15:59Z)

Cross-site request forgery (CSRF) vulnerability in pcsd web UI in pcs before 0.9.149.

Severity: MEDIUM
CVSS v2 base score: 6.8 (AV:N/AC:M/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 8.8, HIGH (CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-352
cwe_name: Cross-Site Request Forgery (CSRF)
description: The web application does not, or can not, sufficiently verify whether a well-formed, valid, consistent request was intentionally provided by the user who submitted the request.
extended_description: When a web server is designed to receive a request from a client without any mechanism for verifying that it was intentionally sent, then it might be possible for an attacker to trick a client into making an unintentional request to the web server which will be treated as an authentic request. This can be done via a URL, image load, XMLHttpRequest, etc. and can result in exposure of data or unintended code execution.
         

Fix information:
repo_url: https://github.com/ClusterLabs/pcs
commit_details: repo_url: https://github.com/ClusterLabs/pcs
author: Ondrej Mular
author_date: 2016-01-29 14:31:37+01:00
msg: fix CSRF vulnerability

 * requests /remote/* (GET and POST) and /run_pcs are accessible only with token authentication
 * all web UI requests are accessible only with session authentication
 * all web UI requests (except for /manage, /managec/<cluster>/main, /permissions ) must
   include header: X-Requested-With: XMLHttpRequest
repository: repo_url: https://github.com/ClusterLabs/pcs
repo_name: ClusterLabs/pcs
description: Pacemaker command line interface and GUI
repo_language: Python
owner: ClusterLabs
file_changes: filename: pcsd.rb
change_type: ModificationType.MODIFY
diff: @@ -121,26 +121,31 @@ $thread_cfgsync = Thread.new {
 
 helpers do
   def protected!
-    if not PCSAuth.loginByToken(session, cookies) and not PCSAuth.isLoggedIn(session)
-      # If we're on /managec/<cluster_name>/main we redirect
-      match_expr = "/managec/(.*)/(.*)"
-      mymatch = request.path.match(match_expr)
-      on_managec_main = false
-      if mymatch and mymatch.length >= 3 and mymatch[2] == "main"
-        on_managec_main = true
+    gui_request = ( # these are URLs for web pages
+      request.path == '/' or
+      request.path == '/manage' or
+      request.path == '/permissions' or
+      request.path.match('/managec/.+/main')
+    )
+    if request.path.start_with?('/remote/') or request.path == '/run_pcs'
+      unless PCSAuth.loginByToken(session, cookies)
+        halt [401, '{"notauthorized":"true"}']
       end
-
-      if request.path.start_with?('/remote') or
-        (request.path.match(match_expr) and not on_managec_main) or
-        '/run_pcs' == request.path or
-        '/clusters_overview' == request.path or
-        request.path.start_with?('/permissions_')
+    else #/managec/* /manage/* /permissions
+      if !gui_request and
+        request.env['HTTP_X_REQUESTED_WITH'] != 'XMLHttpRequest'
       then
-        $logger.info "ERROR: Request without authentication"
+        # Accept non GUI requests only with header
+        # "X_REQUESTED_WITH: XMLHttpRequest". (check if they are send via AJAX).
+        # This prevents CSRF attack.
         halt [401, '{"notauthorized":"true"}']
-      else
-        session[:pre_login_path] = request.path
-        redirect '/login'
+      elsif not PCSAuth.isLoggedIn(session)
+        if gui_request
+          session[:pre_login_path] = request.path
+          redirect '/login'
+        else
+          halt [401, '{"notauthorized":"true"}']
+        end
       end
     end
   end

programming_language: Ruby