The
JavaScript runtime manages memory, removing the need for third-party
developers to worry about traditional coding errors such as buffer and
integer overflows or other memory corruption errors. Not to say that
these problems won’t exist—they will. But they will occur in platform
applications such as WebKit, and memory corruption errors should not be a
primary concern for third-party application developers.
Instead, application
developers must focus on preventing application-level flaws, including
script injection, SQL injection, and business logic flaws. These three
attack classes are real risks on WebOS and take some effort to avoid.
This section outlines the common coding errors, their impact, why they
occur, and how to prevent them. An analysis of unique WebOS behaviors is
also presented.
Script Injection
One of the most common web
application vulnerabilities is cross-site scripting (XSS). This
vulnerability occurs when a web application accepts user data and
inserts that user data directly into a generated web page or AJAX
response. If the user data is malicious and includes JavaScript, the
script will execute in the context of the web application and allow the
user to abuse the user session.
XSS results when the
web application fails to encode or reject the user-supplied data when
generating web pages. When the web browser receives the page, it cannot
differentiate between JavaScript the developer supplied and the
JavaScript that the attacker has injected. Because it can’t tell the
difference, the browser errors on the side of execution and runs any
script it finds. To prevent these vulnerabilities, the web application
must encode user data before inserting it into the web page. The browser
will not treat encoded data as JavaScript and the attacker’s exploit
won’t execute.
WebOS script
injection is very similar to XSS. If attackers provide data to
applications and that data is either treated as JavaScript or inserted
into scene bodies without escaping, the Mojo framework will run the
script as part of the application. WebOS JavaScript is much less
constrained than the web browser sandboxe’s web JavaScript. Once the
attacker’s JavaScript is executing, the attacker can send messages
to the public bus, access per-application private data, and attack the
rest of the device. Script injection in com.palm.* applications is even
more worrisome because these applications can access the private bus and
sensitive data, including text messages and e-mail.
At the time of this writing,
Palm has already released patches for two script injection
vulnerabilities, demonstrating that script injection vulnerabilities are
a concern.
Three broad categories
of script injection affect WebOS: direct evaluation, programmatic
insertion, and template injection. Regardless of category, the severity
is still critical. The categories only differ in the ways in which the
vulnerability’s manifest.
Note that this research is
extremely young, and new forms of script injection will likely appear in
the future. Remember to handle all user data with suspicion, especially
when combining it with executable JavaScript or HTML.
Direct Evaluation
Direct evaluation
vulnerabilities occur when applications take user data and execute it,
either by using the eval statement or by improperly handling the data
when serializing or deserializing objects. JavaScript, and its
associated frameworks, provides many methods to directly execute code.
Because of the flexibility that dynamic execution enables, direct
evaluation is a surprisingly common pattern in modern web applications.
The following two methods are the most common source of direct
evaluation script injection vulnerabilities in WebOS.
eval
The JavaScript eval()
statement accepts a string parameter containing JavaScript, compiles the
parameter into JavaScript bytecode, and then executes the newly
compiled statement. Frameworks commonly use eval() when dynamically
generating classes or creating framework objects. If attackers are able
to insert unescaped data when the eval() statement is being assembled,
the attacker’s JavaScript will be compiled and evaluated as legitimate
user-supplied code. To prevent this vulnerability, do not generate
eval() statements that include user data. Very few WebOS applications
should require using eval(), and its common use may be an indication of
poor design. Prefer designs that do not commonly use eval().
If eval() is the only
option, ensure that the data comes from a trusted source and use the
JavaScript “escape” function when processing untrusted data.
For example, this function is vulnerable to direct script injection via eval:
//user_data contains un-escaped and potentially malicious user-data
var j = eval('executeRequest(' + user_data + ')');
The
developer is intending to call executeRequest with user_data as a
parameter. However, attackers could supply a string for user_data that
includes script. Here’s an example:
After this string is concatenated into the preceding eval() statement, the evil function will be called.
To mitigate this vulnerability, change the code to the following:
//The built-in escape function will render malicious data harmless
var user_data_escaped = escape(user_data);
var j = eval('executeRequest(' + user_data_escaped + ')');
The JavaScript
escape() function encodes JavaScript meta-characters so that they will
not be interpreted as JavaScript when the eval() statement executes.
JSON Injection
Another form
of direct evaluation vulnerabilities occurs when parsing objects
serialized using JavaScript Object Notation (JSON). This notation
describes objects as a series of key/value pairs. An advantage of JSON
is that the serialized blob is actually JavaScript. This makes using
JSON extremely easy because string JSON can be deserialized with the
eval() function. WebOS uses JSON extensively as the message interchange
format for serialization requests.
An object with two properties (key1 and key2) serialized as a JSON string looks similar to the following:
"{
key1 : "value";
key2 : "value2";
}"
Prototype’s evalJSON() method
is used to deserialize this string back to a native JSON object. The
attacker can abuse the deserialization process by supplying objects
containing JavaScript expressions as parameter values.
Here’s a malicious JSON object:
"{
key1 : Mojo.Log.error('Exploited');
key2 : 42;
}"
When
the JavaScript runtime deserializes this object using eval() or
Prototype’s evalJSON(), the attacker-supplied logging statement will
execute. Of course, logging statements are fairly benign. Attackers can
always supply more damaging exploit code.
To mitigate JSON injection,
never use eval() to deserialize JSON objects. Always use Prototype’s
evalJSON() string method and pass “true” for the “sanitize” parameter.
This parameter forces Prototype to reject any property value that
contains an executable statement. Always use Prototype rather than
“rolling your own” because the Prototype library is widely used and has
been well reviewed.
Here’s an example of using the evalJSON() method to correctly ignore JavaScript during JSON deserialization:
var fruits = user_data.evalJSON(true);
Programmatic Data Injection
Script injection can also
occur when data is programmatically inserted into Views by either
manipulating the DOM directly or by calling Mojo functions which update
the DOM. Once the attacker can affect the DOM, they can inject
JavaScript and execute their exploit code.
innerHTML Injection
A simple form of script
injection occurs when unescaped user data is assigned to an HTML
element’s innerHTML property. This property accepts raw HTML and
actually replaces the HTML of the parent element. Attackers can easily
abuse this behavior to inject script tags into the WebOS application’s
DOM.
For example, consider the
following example from a sports application. The “user_data” variable
contains unvalidated and untrusted user data.
var updatedScores = "<b>" + user_data + "</b>";
this.sportsScoreElement.innerHTML = updatedScores;
In general, it is unsafe to
use innerHTML for updating the WebOS DOM. Most of the time, developers
follow this pattern because they are building HTML through string
concatenation. This method of generating HTML is very dangerous and
should be avoided if at all possible.
update() Injection
Many WebOS
applications use the sceneController’s update() method to refresh screen
elements and force a redraw. Unescaped user data must never be passed directly to the update() method because this can allow malicious JavaScript to be injected into the DOM.
Here are two vulnerable examples:
var updated_scores = "<b>" + user_data + "</b>";
this.controller.update($('SportsScores'), updated_scores);
Or more directly:
this.controller.update($('SportsScores'), user_data);
In both of these
instances, the SportsScores element is being updated with user_data,
which may be malicious and provide attackers with the opportunity to
inject script.
Avoid concatenating user data
with HTML tags. Not only is this practice less efficient, but it makes
finding and removing script injection very difficult. The best solution
is to design out the string concatenation.
If that is not possible,
and sometimes it won’t be, then make sure to escape HTML data before
sending it into the DOM. To do so, use Prototype’s escapeHTML()
function. This method replaces all of the potentially dangerous
characters with their “safe” versions. For example, < becomes <.
After this transformation, the < will no longer be interpreted as
part of the document’s structure and attackers will no longer be able to
insert script. The preceding vulnerable snippets could be rewritten as
follows:
var updatedScores = "<b>" + user_data.escapeHTML() + "</b>"; this.controller.update($('SportsScores'), updated_scores);
and
this.controller.update($('SportsScores'), user_data);
Unfortunately, this method is
far from foolproof, and its success relies on never forgetting to apply
the escapeHTML() function to potentially malicious data. Given the size
of modern applications, errors will likely slip through. A still better
option is to use WebOS templates for inserting user data into the DOM.
|
Template Injection
The
final category of script injection flaws are template injection flaws.
Generically, templates are snippets of HTML containing placeholders
where user data can be inserted. They are used heavily within views and
are helpful to developers looking to avoid writing large amounts of
presentation-generation code.
WebOS overloads the term template.
There are actually two types of templates, and their behavior is
similar but different in a scary and impactful way. The two types of
templates are WebOS widget templates and Prototype templates. Most
developers will only use WebOS templates because they are much more
integrated into the overall Mojo framework.
WebOS Templates
When some widgets (such as
the List widget) are used, a formatting template file may be specified.
WebOS will invoke this template for each row that it inserts into the
output list. The template contains HTML with simple placeholders that
will be replaced by values from the template’s model. This system lets
the developer avoid having to write complicated formatting code for
configuring UI elements.
For example, the following
HTML snippet formats will be applied to each list row and cause each
element to have two divs—one for the team_name and one for the
team_score. The contents of the #{team_name} and #{score} elements will
be replaced with data from the row’s model object.
<div class="row textfield" >
<div class="score_tuple">
<div class="team_name">Team Name: #{team_name}</p>
<div class="team_score">Score: #{score}</div>
</div>
</div>
By default, the data
substituted for the #{team_name} and #{score} tokens will be
HTML-escaped. Therefore, script injection vulnerabilities, like the ones
discussed in the “innerHTML Injection” section, will be prevented. This
is obviously a clear advantage templates have over other formatting
mechanisms.
However, the automatic escaping
can be disabled by editing the application’s framework_config.json file
and setting the value of the escapeHTMLInTemplates property to false.
The framework_config.json file is stored in the application’s root
directory and contains a list of properties that configure the Mojo
framework itself. Try to avoid setting this property to false.
Otherwise, the entire application must be reviewed
for locations where unescaped and potentially malicious user data is
formatted into templates. This is a time-consuming and expensive
process, especially when compared to the cost of writing proper
templates.
Instead of globally
disabling HTML escaping, instruct the framework to disable it on a
token-by-token basis. Do this by placing a hyphen (-) at the beginning
of the replacement tokens. The framework will skip HTML-escaping these
elements.
Here’s an updated version of the preceding team_name div. The #{team_name} replacement token will not be escaped.
<div class="team_name">Team Name: #{-team_name}</p>
Use this behavior
sparingly. If an element is excluded from HTML escaping, ensure that
models used for formatting do not contain any unescaped user data.
Prototype Templates
The Prototype JavaScript
framework, included with WebOS, has its own template functionality that
is very similar to standard WebOS view templates. However, these
templates are not governed by the escapeHTMLInTemplates property.
Therefore, any data formatted into Prototype templates must be manually
escaped.
For example, the following template-formatting routine is vulnerable to script injection when user_data contains malicious data:
var score_template = new Template("<b> #{new_score} </b>");
sports_score = score_template.evaluate({"new_score" : user_data});
this.controller.update($('SportsScores'), sports_score);
To make this function safe, manually escape the data using Prototype’s escapeHTML() function.
Local Data Injection
Data from web pages, e-mails,
and text messages is obviously malicious, but an additional, and often
forgotten about, attack surface is the local one. With the rise of
mobile malware, it is highly likely that users will install a malicious
application at one point or another. WebOS makes some attempts to
protect applications from each other: Sensitive data, such as e-mails
and SMS messages, is directly accessible only through the private bus,
and each application is able to store private data using either the
cookie or depot storage API. Therefore, in order for mobile malware to
compromise another application’s data, the malware must find a way to
inject script into the target application.
Unfortunately,
there are several ways this may happen, and they are not all well
documented. This section outlines the various vectors available to
malware attempting to inject script.
Application Launch Parameter Script Injection
One method attackers may use to
inject script is by providing parameters containing script values.
These parameters will be passed to the StageAssistant and/or
AppAssistant when the application starts. The system provides no
guarantees about the quality of this data, and parameter data certainly
cannot be trusted.
To launch another
application, applications dispatch a “launch” service request to the
ApplicationManager service. Here’s an example:
this.controller.serviceRequest("palm://com.palm.applicationManager",
{
"method" : "launch",
"parameters" : {
"id" : "com.isecpartners.movietimes",
"params": {
movie_title : "Indiana Jones",
server_url: "http://www.isecpartners.com/movies"
}
}
});
All applications are
permitted to launch any other installed application and supply any
parameter values they wish. Applications need to handle potentially
malicious launch parameters. If the launched application doesn’t handle
the data appropriately—perhaps by improper use of templates or the
update method—the malware could inject JavaScript that will execute in
the context of the target application.
There are legitimate
uses for launch parameters. For example, a movie application may take a
“movie_title” parameter that it uses to search the Internet for movie
show times. Because malware may have caused the application launch, the
movie application must be careful about how it treats the “movie_title”
data. Otherwise, formatting the search query into a template or
evaluating it as JSON will likely result in script injection.
Script injection is
not the only concern; malicious values may be supplied to exploit the
application’s business logic. For example, our movie application could
take a “server_url” parameter that tells the application which server to
run the search query against. Assume that along with this search
request, the application sends an authentication token. Obviously, an
attacker would like to gain access to this token. By
supplying their own value for the “server_url” parameter, the attacker
may be able to force the application to post the attacker’s malicious
server. This technique could be used to harvest a large number of
authentication tokens.
Do not trust launch
parameter values and, if possible, limit the set of allowed values to a
predefined whitelist. Avoid sending data, especially sensitive data, to
launch parameter-specified server addresses. If a request will leak
sensitive data, consider asking the user before issuing the request.
To test an application’s
resistance to malicious launch parameters, start the application using
the luna-send command-line tool. This tool must be run from a WebOS
command prompt and enables sending serviceRequests directly to services.
To use luna-send to launch an application, do the following:
Open a novaterm shell to the device.
Run the following command. The terminal requires the additional backslashes be used for escaping quotes.
luna-send -n 1 "palm://com.palm.applicationManager/launch"
"{\"id\":\"com.isecpartners.helloworld\",
\"params\":\"{foo:\\\"bar\\\"}\"}"
Directly Launching Scenes
A slightly less
well understood boundary is the scene boundary. Quite simply, any
application can push a scene from any other application onto its scene
stack. When this happens, the application that owns the scene is started
and the execution context switches to the newly pushed scene.
This behavior is
legitimately used throughout WebOS to elevate privileges and to provide a
consistent user experience. For example, applications are prohibited
from using the Camera plug in directly, but they can push the capture
scene from the Camera application onto their stack. When they do so, the
Camera scene runs and provides the user interface and permissions for
taking the camera shot.
The Camera capture scene is an
example of a scene that was designed to be included by other
applications. However, not all scenes are designed this way, and
attackers may be able to abuse scenes by invoking them out of order or
supplying malicious parameters. Vulnerabilities resulting from the
direct instantiation of scenes are similar to application launch
vulnerabilities. Applications can invoke a scene directly using the
Mojo.Controller.StageController.pushScene(scene, args) method, as shown
here:
this.controller.stageController.pushScene(
{ appId: 'com.isecpartners.movie_app', name: 'PurchaseScene' },
{ tickets: "2"});
The
second (args) parameter to the pushScene() method is an object that
will be passed to the PurchaseScene’s scene assistant. Just like
parameters passed on application launch, scene parameters must not be
trusted. Also be aware that your scene may be called out of order.
Imagine the following scenario:
A
movie application allows you to look up nearby movie times and purchase
tickets. To ease the purchase process, the movie application stores
your account information.
When
you make the purchase, the application takes you through a three-step
process: Search Movies, Select Theater and Time, and Purchase.
The Purchase scene takes a movie ticket ID and the e-mail address to send the tickets to as scene parameters.
When the Purchase scene starts, it performs the purchase automatically and then shows the results to the user.
Malware can abuse this
process by skipping the first two scenes and directly invoking the
Purchase scene. Because the application didn’t verify the e-mail address
parameter, the tickets are sent to the attacker’s e-mail address
instead of to the legitimate user.
The process may sound
contrived, but vulnerabilities extremely similar to this have been found
in several WebOS applications. Unfortunately, being vigilant is the
only solution. Always design scenes so that they are resistant to
malicious parameters and do not execute sensitive actions without first
asking the user for confirmation.
Opening Documents and Service Requests
Two additional
methods for launching applications and communicating between
applications involve using the Application Manager’s “open” method and
directly making a serviceRequest. In the current version of the SDK,
third-party applications are unable to register to be launched using
either of these methods. If third-party services or document handling
are ever allowed, though, risks similar to those outlined in the
previous two sections will likely apply.
Application Packaging
Applications are packaged
using the Itsy Package Manager System (ipkg). This is a commonly used
application distribution format for embedded devices, and it bundles the
entire application into a single easy-to-distribute file. The package
files have the extension
.ipk and are actually tar’d and gzip’d files (.tar.gz). To uncompress
IPKs on Windows, simply use an extractor, such as 7-Zip, to extract the
package’s contents.
IPKs may have an embedded
signature, although Palm has not yet released the details on their
signing process. Very few, if any details are currently available about
the signatures that will be required for WebOS applications. The best
guidance comes from the Terms of Service (http://developer.palm.com/termsofservice.html),
which states that “applications which access or make use of Palm’s APIs
may not be installed or used on Palm Devices, except in a test
environment, without first being signed by a certificate issued by or
for Palm.” In the future, developers will likely have to register with
Palm and receive a developer certificate. Applications currently being
distributed through the App Store are signed with a certificate owned by
Palm. Details about this process will soon be added to the Palm
Developer Portal, so check there for the most current information (http://developer.palm.com).
The package embeds the
signature as three disjointed files: a certificate in PEM format, a
public key, and a detached signature. It is unclear which content this
signature covers, but it likely includes all files and directories
within the IPK.