Exfiltrating Data in GoogleSheetsPublished: Jan 11th, 2023 (6 months ago)

A good tool that companies use to track and visualise data is through spreadsheet software. Today, I'm going to demonstrate a quick way a malicious employee can still have access to the data even if they get removed from the company and their access is revoked - and it's somewhat hard to detect.

This is harder to detect than an AppScript because this technique can be put in 1 cell anywhere in the spreadsheet and formatted visually to be pretty much invisible (ie: white text on a white background).

Let's assume we have a sheet with sensitive data like this:

Example Sheet
An example sheet holding sensitive data

It would be great if someone could exfiltrate all the data in these columns for every data update, wouldn't? And it doesn't require having an "Apps Script", it is more incognito than that.

A simple formula to hit a remote server with importdata() and concatenating the data ranges to make it easier to parse is as simple as this. Formula
Formula to send data to a remote server
Now, on every cell update within range A:C, the remote server gets hits. Exfiltrated
Data exfiltrated to a remote server
We can see that within the data query string, the data is present from the Google Sheet (even after adding a new row for Client C). And to visualise, we can write a simple JS 1 liner to organise the data more readable...
decodeURIComponent("Client%2CA%2CB%2CC---Budget%2C%24%20500.0K%2C%24%2075.0K%2C1.0M---Contact%20Email%2Ccontact%40company-a.com%2Ccontact%40comany-b.com%2Ccontact%40comany-c.com").split("---").map((d,i) => {exfil[i] = {head: d.split(",")[0], data: d.split(",").slice(1)};});

Now all the data is in an array and much easier to manage - this could be on the remote server listening for requests from the spreadsheet and used to organise the data and then sent/stored in a database.

[
    {
		"head": "Client",
		"data": [
			"A",
			"B",
			"C"
		]
	},
	{
		"head": "Budget",
		"data": [
			"$ 500.0K",
			"$ 75.0K",
			"1.0M"
		]
	},
	{
		"head": "Contact Email",
		"data": [
			"[email protected]",
			"[email protected]",
			"[email protected]"
		]
	}
]

Also, the request to the remote server comes from Google servers and not the client looking at the spreadsheet.

Detection

We can deploy an AppScript to detect if the sheet has formulas AND if the formula includes capability of data exfiltration. Then we can manually check if the formulas are doing anything nefarious.

function checkForPossibleExfil() {
  const regex = new RegExp("importdata");
  var data = SpreadsheetApp.getActiveSheet().getDataRange().getFormulas();
  for(i in data) {
    for (var j in data[i]) {
      if(data[i][j]) {
        if(data[i][j].match(regex)) {
          Logger.log(data[i][j]);
        }
      }
    }
  }
}