Google Spreadsheet Vuln - CSRF and JSON Hijacking allows data theft

Last Modified: Wed, 26 Oct 2016 03:09:37 +0000 ; Created: Wed, 26 Oct 2016 03:04:08 +0000

In October of 2015 I discovered a JSON + CSRF hijacking vulnerability with a Google API related to spreadsheet graph rendering. Using this vunlerability an attacker could steal the content of a spreadsheet from a target victim without having permission to the Google Drive file.

Impact of the vulnerability:

When exploiting the vulnerability against target victim(s) from the Internet, an attacker is able to bypass the Sharing settings ACL restrictions on Google Drive native spreadsheets. To clarify, an attacker who is not authorized to access a Google Drive file, as shown in the below image, is able to exploit the vulnerability to bypass the Google security protections:

Nature of vulnerability

This is not the first time that Google has had a JSON Hijacking vulneraiblity that allowed for customer data exposure.

The nature lies in a software design flaw in Google Drive APIs that have caused an OWASP TOP 10 (2013) - A8-Cross-Site Request Forgery (CSRF) vulnerability that allows for JSON Hijacking.

The vulnerability is not difficult to exploit. One past example is Google Gmail, which has been vulnerable to CSRF+JSON hijacking before in publicly released vulnerability exploits.

For the Gmail JSON hijack Google was able to remediate the issue by appending an infinite while() loop to their JSON to cause target victims' browsers to crash during exploit attempts, which stops the attacker.

However, the nature of this Google Drive vulnerability is such that Google cannot simply apply a quick fix to the JSON. The necessary fix requires complex changes to Google Drive in order to avoid breaking product functionality.

In Google's case they opted to retire the old API and replace it with a new one that required additional security permissions. This required developers who used the API to update their code.

Example Attack Scenario (one of many):

Business has a spreadsheet with confidential information in it.

Spreadsheet is only shared with authorized individuals in the company.

An employee leaves the company so their access is removed and shared passwords/pins/secrets in the spreadsheet are changed after they leave denying them access or knowledge of the data.

The new sharing settings showing the user's access removed.

The force be with you

The now removed employee desires to obtain unauthorized access to the spreadsheet. They know that Mr. [email protected] likes to frequent a website that allows anyone on the internet to publish comments with HTML markup. This is known as a watering hole attack as the attacker simply waits for their victim instead of attempting a more active attack like phishing or otherwise.

Screenshot of victim/user visiting the website.

Unknown to the target victim (who is not even the document owner) they have just sent a copy of the spreadsheet data to the attacker.

What the stolen data looks like (not seen by the victim):

The attacker sees the data as shown

How is this possible?

The API in question did not require an OAuth token from a referring application and allows other sites to make cross site requests. This allows any website to call the API and obtain the spreadsheet data of a victim who is already logged into their Google account.

Because the response returned is JSON it is possible using JavaScript to have the web browser parse the information into an object that gets submitted to an attacker's choice web server.

Simple proof of concept exploit code can be found in the following files:

An example of the code that parses the hijacked JSON:

		var google = new Object();
		google.visualization = new Object();
		google.visualization.Query = new Object();
		google.visualization.Query.setResponse = function(goods) {
			google.response = JSON.stringify(goods, undefined, 2);

The reply from the API service returns a JavaScript object with the data in JSON encoded format. That data is available to our cross-origin page in the JSON object so we simply submit it to wherever we want.


Date Event
October 29, 2015 Report sent to Google
October 30, 2015 Google acknowledges security bug is a legit threat attack vector. Asked Google for ETA on fix.
November 6, 2015 Google replies: "...the product team is hoping they can finally make a change that won't break a lot of users sometime next year. As you can hopefully understand, the issue requires careful coordination as there are many users relying on this behavior today.. but I'm happy to say that there has been progress on this, and it will be fixed."
January 5, 2016 Asked Google for ETA on fix
January 6, 2016 Google replies still no ETA on a fix
January 14, 2016 Asked Google for guess of ETA on fix
January 27, 2016 90 days with no fix nor ETA
February 5, 2016 Requested update from Google (99 days since issue reported; 30 days since last communication from Google)
February 15, 2016 Google responds with no ETA on a fix
February 25, 2016 Asked Google for reference number to share with their business support to request a fix in the product.
February 25, 2016 Google responds with bug # for reference
March 17, 2016 Asked Google for ETA for a fix
April 1, 2016 Google responds "Sorry, we don't have a timeline for remediation to share yet. We'll let you know once we have more information."
April 26, 2016 180 days with no fix nor ETA
May 31, 2016 215 days with no fix nor ETA. Asked for ETA.
June 3, 2016 Google responds that they are still working on the fix and are managing the transition slowly to avoid affecting a number of users adversely.
August 17, 2016 Asked for ETA update
August 17, 2016 Google responds same day that they have turned on a warning message for most users that API is changing.
September 13, 2016 Confirmed that the warning message was appearing
September 15, 2016 322 days later confirmed that final fix was working and the vulnerability was closed.

Fix Near

After looking at the issue for quite some time Google decided that trying to code a backwards compatability fix wasn't going to be the best route. They opted to decommission the old API and replace it with a new one instead. This made the most sense, in my opinion, at resolving the issue versus trying to catch all the corner cases that might still allow a security bypass.

An alert started to appear telling the user that use of the OLD api by the website/application would stop working soon.

Final Fix Confirmed

I confirmed the issue was finally fixed on September 15, 2016.


It took a while but keeping monthly contact with Google did see the issue fixed and spreadsheet data once again secured. At times it was difficult to get a response from Google, but I felt it was better to get the issue fixed properly rather than try to pressure them with disclosure. The complete fix was a bit involved since Google had to figure out how to minimize impact to functionality too.

I was added to Google's Hall of Fame for my finding. You can find my profile here.