Exposed authentication tokens of London Cycling Campaign

Posted on:

This is a legacy post which is now being made public. In May 2017, the London Cycling Campaign had an insecure API endpoint which leaked user data. This post serves as a reflection to the issues I found, all of which have been fixed.

This is not good, London Cycling Campaign (LCC) have exposed their 11,000 members (source) and countless petition signatories details by hard coding a privileged authentication token in two web applications.

This year, as with every other year, the Open Web Application Security Project Top 10 Application Security Risks lists Broken Authentication and Session Management as number 2, they provide the following definition:

"Application functions related to authentication and session management are often implemented incorrectly, allowing attackers to compromise passwords, keys, or session tokens, or to exploit other implementation flaws to assume other users’ identities (temporarily or permanently)."

Attackers can use leaked credentials to temporarily impersonate a user. Therefore, developers should be careful when disclosing authentication tokens in a public setting. This is the issue we have with the LCC.

Any individual with a basic level programming would be able to exploit this issue. If they consulted the public developer documents they could gain further access to personal data

LCC is an independent charity lobbying for better conditions for cycling in London. I myself am a keen cyclist, I support the cause, and have multiple friends who are members. They do a very good job in trying to promote and improved cycling, infrastructure and awareness. I considered joining them a couple of months ago, after seeing their security practises I quickly decided against it. And this is where my story starts.

Previous Reporting

I first contacted LCC in May (14/05/2017) to report several issues related to the site, it took them 10 days to provide a response (25/05/2017). The response stated that they would address my concerns in the following weeks. The two main issues were.

No site-wide HTTPS (TLS)

Registration and login forms do not use https, therefore exposing the user to an increased risk of man-in-the-middle attacks. However, LCC do use https, just on selective pages (donations, sign up or membership etc) but not on the login page. This suggests an inconsistent approach to https.

Incorrect HTTPS (TLS) implementation on payment page

That is right, they are asking for payment details on an insecure form. Simply deploying a Content Security Policy would fix this issue. Remember the URL, we will come back to this later.

With the response from LCC stating they were going to fix the issues, I left it at that, made a note to come back/check and moved on.

The Leaky Authentication Tokens

More than a month after reporting, I went back to LCC site to find all the same issues existed, nothing had changed. While browsing the site I discovered they had a list of Campaigns.

I decided to take a look and see if they followed the same practices seen on lcc.org.uk.

Sign for Cycling (http://signforcycling.org)

The site is aimed at getting people to sign a petition which encourages the next London Mayor to prioritise cycling. It collects First name, Last name, Post code and email address. I was curious to see how it sent these details. So, I decided to use Google Chrome Inspector and viewed the JavaScript file (app.js).

    mud.data = {
        getData: function(){
            $.ajax({
                url:'http://petition.lcc.org.uk/ea-dataservice/data.service?service=EaDataCapture&ea.client.id=1745&campaignId=47670&token=8ca4063b-0231-4ba8-a6a2-c363a8316e9f&startRow=0&resultType=summary&contentType=json',
                dataType: 'jsonp',
                timeout:3000,
                crossDomain: true
            }).then(function(data){
                // get supporter count
               var supporterCount = data.rows[0].columns[5].value;
               $('.block--supporters__number').text(supporterCount);
            }).fail(function(){
            });
        }
    }

Notice anything amiss here? Yes, that is a GET request to a http address (no https, nor is it enforced when loading) with an authentication token 8ca4063b-0231-4ba8-a6a2-c363a8316e9! Usually, these are stored in the back-end and are not shared publicly (with a few exceptions).

This website was intended for 2016 Mayoral elections. Why it is still live? Why does it have hard-coded authentication token on the front-end?

Space4Cycling (http://space4cycling.org)

Great initiative. Shame to see that my local area, Kilburn, has made "No significant progress made to achieve this cycle improvement" which is disappointing. I again was interested to see how they obtained the information. Again, they were using an authentication token in the main JavaScript file.

    function getMessages() {   
    url ='http://e-activist.com/ea-dataservice/data.service?service=EaDataCapture&token=8ca4063b-0231-4ba8-a6a2-c363a8316e9f&campaignId=23504&contentType=json&resultType=summary';
    $.ajax({
     url: url,
     dataType: 'jsonp',
     success:function(json){         
		 $('.messages').html(json.rows[0].columns[4].value);
     }
     });

Again, another GET request. The eagle-eyed among you will notice that different domain this time(http://e-activist.com/), but the same authentication token, 8ca4063b-0231-4ba8-a6a2-c363a8316e9f. This concerned me, different API endpoints yet the same token is valid.

Using the Leaky Authentication Tokens

I decided to dig a little deeper and fired up Postman.

Starting with Sign for Cycling (http://signforcycling.org) I put the request into Postman, simple copy and paste from the JavaScript file:

GET: http://petition.lcc.org.uk/ea-dataservice/data.service?service=EaDataCapture&ea.client.id=1745&campaignId=47670&token=8ca4063b-0231-4ba8-a6a2-c363a8316e9f&startRow=0&resultType=summary&contentType=json

and the output was:

This is the same information they display on the Sign for Cycling website, nothing new. I noticed the parameter resultType, this peaked my interest. Performing GET url ... &resultType=, the system helpfully provided some debugging, supply either detailed or summary parameter. Based on the developer documentation available online (more on this later), I was confident that modifying the query (&resultType=detailed) will only provide brief summary data:

I was able to access the details of all signatories to the specific petition (name, post code, region) except their email address. This information was provided over http, totally insecure. Granted last name and email were not provided, but this information should not be accessible.

If you alert the parameter campaignId, you can access information related to other campaigns. At this pointed I recalled seeing a similar syntax to (used above) data.service?service=EaDataCapture elsewhere.

Remember this from the payments page?

The syntax looks eerily similar, but it is on a different domain (netdonar.net). Surely the authentication token will not work here. It does, all you had to do was make a GET request by adding the authentication token.

The same authentication token is valid on http://petition.lcc.org.uk, https://netdoner.net and http://e-activist.com/. This suggests that API calls resolve to the same location. This is very bad practise, beside sharing the token you should always use separate tokens for different services!

This is the membership information (very limited) for those who joined the most recent campaign. Note: I use the word membership campaign, as the system uses campaign to denote petitions and other types which are defined in the admin panel.

The information is only summary data (no actual address, payment details etc) was viewed or accessed.

I assumed that LCC was using a prebuilt software platform, I Google'd ea-dataservice and discovered the following developer documentation. It's what you would expect from dev docs, nothing stood out except one. They provide an export form, if you have the authentication token, you can download all the information stored for the specified client. The export service was not tested.

The developer documents also shed light on what else you can gain access to, such as a fundraising summary, how about an email address or what about the persons postal address. All is accessible by simply changing the syntax of the URL.

Summary membership, petitions and finances was accessible via a the same publicly available authentication token.

It is clear what information could be accessed with this authentication token - personal information related to members, non-members and LCC finances. I have encouraged LCC to perform a full audit of their systems to ensure no malicious access to the data.

Based on my limited interactions with the system, the public JavaScript documents, developer documents and code comments I believe this authentication token would have provided me with full, unrestricted access to all data held on their systems if I had chosen to go down that route.

Conclusion

All the API end points were publicly accessible, all that was required is to add the token= syntax and bingo, you've got access to the data. I wonder who else has noticed their release and accessed this information - at a much deeper level. It is important that, as developers, we minify our code and remove all comments which may describe the system and allow an attack to form a map of the internal network.

This is a prime example, that although you do not leak data from your central site (in this case LCC) information provided on other sites can easily provide a back door. Think back to Sign for Cycling, which was released as part of the 2016 Mayoral contest, why has it not been taken down or replaced? Why were authentication tokens hard coded into the site? It is so important to be aware of the digital estate organisations manage and audit it regularly. Do not release your authentication tokens, add expiry to these tokens and restrict access.

London Cycling Campaign
comments powered by Disqus