CVE-2020-14213 (published 2020-06-16T23:15Z)

In Zammad before 3.3.1, a Customer has ticket access that should only be available to an Agent (e.g., read internal data, split, or merge).

Severity: MEDIUM
CVSS v2 base score: 5.5 (AV:N/AC:L/Au:S/C:P/I:P/A:N)
CVSS v3 base score: 5.4, MEDIUM (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N)

Weakness information:
cwe_id: CWE-862
cwe_name: Missing Authorization
description: The product does not perform an authorization check when an actor attempts to access a resource or perform an action.
extended_description: 
            Assuming a user with a given identity, authorization is the process of determining whether that user can access a given resource, based on the user's privileges and any permissions or other access-control specifications that apply to the resource.
            When access control checks are not applied, users are able to access data or perform actions that they should not be allowed to perform. This can lead to a wide range of problems, including information exposures, denial of service, and arbitrary code execution.
         
         

Fix information:
repo_url: https://github.com/zammad/zammad
commit_details: repo_url: https://github.com/zammad/zammad
author: Rolf Schmidt
author_date: 2020-05-05 10:50:03+02:00
msg: Enhancement: Added authorization checks for ticket controller actions: ticket_customer ticket_history ticket_related ticket_recent ticket_merge ticket_split.
repository: repo_url: https://github.com/zammad/zammad
repo_name: zammad/zammad
description: Zammad is a web based open source helpdesk/customer support system
repo_language: Ruby
owner: zammad
file_changes: filename: tickets_controller.rb
change_type: ModificationType.MODIFY
diff: @@ -6,7 +6,7 @@ class TicketsController < ApplicationController
   include ChecksUserAttributesByCurrentUserPermission
   include TicketStats
 
-  prepend_before_action -> { authorize! }, only: %i[create selector import_example import_start]
+  prepend_before_action -> { authorize! }, only: %i[create selector import_example import_start ticket_customer ticket_history ticket_related ticket_recent ticket_merge ticket_split]
   prepend_before_action :authentication_check
 
   # GET /api/v1/tickets

programming_language: Ruby
filename: tickets_controller_policy.rb
change_type: ModificationType.MODIFY
diff: @@ -1,5 +1,6 @@
 class Controllers::TicketsControllerPolicy < Controllers::ApplicationControllerPolicy
   permit! %i[import_example import_start], to: 'admin'
   permit! :selector, to: 'admin.*'
+  permit! %i[ticket_customer ticket_history ticket_related ticket_recent ticket_merge ticket_split], to: 'ticket.agent'
   permit! :create, to: ['ticket.agent', 'ticket.customer']
 end

programming_language: Ruby
filename: ticket_spec.rb
change_type: ModificationType.MODIFY
diff: @@ -1793,6 +1793,10 @@ RSpec.describe 'Ticket', type: :request do
         created_by_id: 1,
       )
 
+      authenticated_as(customer_user)
+      get "/api/v1/ticket_split?ticket_id=#{ticket.id}&article_id=#{article.id}&form_id=new_form_id123", params: {}, as: :json
+      expect(response).to have_http_status(:unauthorized)
+
       authenticated_as(agent_user)
       get "/api/v1/ticket_split?ticket_id=#{ticket.id}&article_id=#{article.id}&form_id=new_form_id123", params: {}, as: :json
       expect(response).to have_http_status(:ok)
@@ -1918,6 +1922,10 @@ RSpec.describe 'Ticket', type: :request do
         customer_id: customer_user.id,
       )
 
+      authenticated_as(customer_user)
+      get "/api/v1/ticket_merge/#{ticket2.id}/#{ticket1.id}", params: {}, as: :json
+      expect(response).to have_http_status(:unauthorized)
+
       authenticated_as(agent_user)
       get "/api/v1/ticket_merge/#{ticket2.id}/#{ticket1.id}", params: {}, as: :json
       expect(response).to have_http_status(:ok)
@@ -2068,7 +2076,39 @@ RSpec.describe 'Ticket', type: :request do
       expect(json_response['assets'].class).to eq(Hash)
       expect(json_response['assets']['User'][customer_user.id.to_s]).not_to be_nil
       expect(json_response['assets']['Ticket'][ticket1.id.to_s]).not_to be_nil
+
+      authenticated_as(customer_user)
+      get "/api/v1/ticket_history/#{ticket1.id}", params: {}, as: :json
+      expect(response).to have_http_status(:unauthorized)
+    end
+
+    it 'does ticket related' do
+      ticket1 = create(
+        :ticket,
+        title:       'some title',
+        group:       ticket_group,
+        customer_id: customer_user.id,
+      )
+
+      authenticated_as(agent_user)
+      get "/api/v1/ticket_related/#{ticket1.id}", params: {}, as: :json
+      expect(response).to have_http_status(:ok)
+
+      authenticated_as(customer_user)
+      get "/api/v1/ticket_related/#{ticket1.id}", params: {}, as: :json
+      expect(response).to have_http_status(:unauthorized)
+    end
+
+    it 'does ticket recent' do
+      authenticated_as(agent_user)
+      get '/api/v1/ticket_recent', params: {}, as: :json
+      expect(response).to have_http_status(:ok)
+
+      authenticated_as(customer_user)
+      get '/api/v1/ticket_recent', params: {}, as: :json
+      expect(response).to have_http_status(:unauthorized)
     end
+
   end
 
   describe 'stats' do
@@ -2213,7 +2253,7 @@ RSpec.describe 'Ticket', type: :request do
       end
 
       context 'as authorized customer', authenticated_as: -> { customer_authorized } do
-        include_examples 'has access'
+        include_examples 'has no access'
       end
 
       context 'as unauthorized customer', authenticated_as: -> { customer_unauthorized } do

programming_language: Ruby