Thursday, May 27, 2021

Mobile Screenshot Prevention Cheatsheet - Testing and Fixing

Mobile Screenshot Prevention Cheat Sheet - Testing and Fixing



The following article will explain how to test mobile applications against any implemented screenshot prevention mechanism and then it will try to propose mitigations to such problem according to the context.

The following article is the second part of Mobile Screenshot Prevention Cheat Sheet - Risks and Scenarios published on IMQ Minded Security blog.

TLDR; None of the proposed solutions will provide a full protection against screenshotting. Therefore all of them shall be considered as mitigations.

Auditing Screenshot Prevention

In this section we will focus on testing and preventing mobile screenshot via static (e.g. perform a secure code review or a mobile application reverse engineering task) and dynamic (e.g. test a mobile application in its execution environment) contexts.

First things first: anyone approaching mobile application security should carefully read the OWASP Mobile Security Testing Guide (MSTG)[1][2].

Static Analysis

Assuming we have the source code of a mobile application and we have to check if the app implements any mitigation against screenshot attacks, either user or system generated. 
What should we look for?

Android

Usually the common remediation is to set FLAG_SECURE to LayoutParam, hence the first step is to search for it in the codebase. 
But, if we have access to the source code of an application somehow decompiled from a packaged application, it might be possible that the FLAG_SECURE keyword could have been obfuscated or replaced with its integer value.

Therefore, the values to search for, are:
  • FLAG_SECURE (high level symbol)
  • 8192 (numeric value)
  • 0x00002000 (hexadecimal numeric value)
If any of those values are found, it must be checked whether it is used as parameter set to the WindowManager LayoutParams

It should be noted that this flag must be set in all the Android Activities involved in the application and it would be effective for both user and system generated screenshots.

iOS

Since in iOS it is not possible to prevent user generated screenshots, we have to search for any code related to the notification system which can be used to provide awareness about the taken screenshot.
It is then possible to search for the following keywords:
  • userDidTakeScreenshotNotification (Swift)
  • UIApplicationUserDidTakeScreenshotNotification (Objective-C)
If any of the following keywords is found, it would be possible to then observe a function callback being executed after a user generated screenshot has been created.

On the other hand, for system generated screenshots, it would be necessary to analyze pieces of code which are responsible to handle the transitions between foreground and background application statuses.
Since the system generated screenshots are generated just before the application has been put in the background status, in these portions of code, if any remediation has been implemented, we are expecting to find some mechanism which would hide or obfuscate sensitive parts of the UI just before the screenshot has been generated.
It would then be possible to search for the possible values:
  • applicationWillResignActive (here we should find pieces of code hiding parts of the UI, e.g. by setting the hidden property on a view)
  • applicationDidBecomeActive (here we should find pieces of code showing parts of the UI, e.g. by unsetting the hidden property on a view)

    Dynamic Analysis

    TLDR; Let's try to generate a screenshot of the application and let's check if the application UI is shown in the device task manager.

    User Generated Screenshots

    First of all, let's focus on how we can generate a screenshot of our running application. Basically it will depend on which OS and device type we are testing.

    On a variety of Android devices, it is possible to generate a screenshot by using device specific combo keys. The most common combinations are:
    • VOLUME DOWN + POWER
    • VULUME UP + POWER
    It would be also possible to generate a screenshot by using the function tile in the system tray, but it strongly depends on the OS customizations.

    If we are using the Android emulator, we can use the specific button on the emulator toolbar:


    On iOS it depends on which physical keys are available on the target device. The two possible combo keys are:
    • POWER + HOME
    • VOLUME UP + POWER
    Finally, if we are using the iOS simulator, we can use the combo key CMD + S or we can use the drop down menu entry under File -> New Screen Shot.

    In all the above cases, the positive probe is the successful generation of a screenshot containing the same views actually on the device's screen, without any kind of modification and without any kind of notification generated on the device.

    If the running application is properly secured, we will see the following notification being generated on Android:

    On the other hand, since in iOS it is not possible to prevent a user to generate a screenshot of the application, if the running app is implementing a mitigation against screenshot generation it is not actually possible to define which behavior a pentester would see. 
    Some possible mitigations are:
    • The screenshot is partially obfuscated
    • A notification warns the user about the just generated screenshot
    • The application shows a message which informs the user about the just generated screenshot
    Beware that also a transparent and invisible mechanism could be implemented on iOS in order to warn the user about the screenshot generation, so dynamic analysis could not be sufficient in order to fully understand if an app is implementing some mitigation mechanism.

    System Generated Screenshots (AKA Task Manager Screenshots)

    On both the operating systems, the test is the same and is pretty straightforward:
    • Open the target application
    • Send the application in background by pressing the HOME button
    • Open the system task manager (e.g. by tapping the dedicated button on Android or by double tapping the home button on iOS, etc)
    At this point, if an application screenshot is shown identical as it was possible to observe when the app was in foreground, it means that no prevention mechanism was implemented.
    On the other hand, if a blank screen is shown or if the image is partially obfuscated, it does mean that the app is actually implementing some sort of full or partial prevention mechanism against system generated screenshots.

    Fixing Screenshot Issues


    Which APIs are provided to prevent or mitigate the issue?


    Android

    One flag to rule them all.
    Basically Android offers the LayoutParam FLAG_SECURE [3] which can be used to prevent both user and system generated screenshots.

    Simple and straightforward.

    iOS

    On iOS the developer is not provided the ability to prevent user generated screenshots. This is due to the strict UI/UX guidelines provided by Apple which are preventing developers from interfering with the standard OS behavior.
    Two kinds of APIs are interesting from the security point of view, when talking about screenshot prevention mechanisms.

    For user generated screenshots it is possible to use the userDidTakeScreenshotNotification [4] system wide notification. In this way, it would be possible to run code just after a screenshot was generated. 
    The most common approach is to generate a notification or an alert which warns the user about the generated screenshot. This would inform the user and will raise a red flag in case of the screenshot was not explicitly and voluntarily generated.

    On the other hand, for system generated screenshots, it is possible to refer to the applicationWillResignActive [5] application's lifecycle callback. This callback is invoked just before any application is moved from the foreground to the background and enables developers to execute code just before the system generated screenshots are created.
    These operations can include:
    • Putting an overlay over the whole application window
    • Hiding/masking sensitive parts of the UI
    • Navigating to other application parts which are not showing sensitive information

    Stop talking! Show me the code!

    This section is intentionally left without any comment, since explanations have been provided in the above sections.
    Just the source code that can be used in various scenarios and situations.
    Pro tip: If some data is VERY sensitive, such as the combination of a credit card PAN, its PIN code and the CVV2 code, don't put it in your views. If some data is not displayed on the screen, it can't be screenshotted!

    But since in the real world such approach is very often not applicable, let's dig into some mitigation implementations.

    Android

    Protects from:
    • User generated screenshots: ✔️
    • System Generated screenshots: ✔️

    Kotlin

    import android.os.Bundle
    import android.support.v7.app.AppCompatActivity
    import android.view.WindowManager

    class MainActivity : AppCompatActivity() {
      override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        window.setFlags(
          WindowManager.LayoutParams.FLAG_SECURE,
          WindowManager.LayoutParams.FLAG_SECURE
        )
      }
    }


    Java


    import android.os.Bundle;
    import android.view.WindowManager;

    public class MainActivity extends Activity {

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            getWindow().setFlags(
                WindowManager.LayoutParams.FLAG_SECURE,
                WindowManager.LayoutParams.FLAG_SECURE
            );
      }
    }


    References: [6]

    iOS

    Protects from:
    • User generated screenshots: 🤷 (partial)
    • System Generated screenshots: ❌

    Swift

    NotificationCenter.default.addObserver(
        forName: UIApplication.userDidTakeScreenshotNotification,
        object: nil,
        queue: .main) { notification in
            //Notify the user, log the action, etc...
    }

    Objective-C

    NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
    [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationUserDidTakeScreenshotNotification
                                                      object:nil
                                                       queue:mainQueue
                                                  usingBlock:^(NSNotification *note) {
                                                     //Notify the user, log the action, etc...
                                                  }];

    References: [6] [7]


    Protects from:
    • User generated screenshots: ❌
    • System Generated screenshots: ✔️

    - (void)applicationWillResignActive:(UIApplication *)application {
        
        // hide main window
        self.window.hidden = YES;
    }

    - (void)applicationDidBecomeActive:(UIApplication *)application {
        
        // show window back
        self.window.hidden = NO;
    }

    References: [8]

    React Native

    Protects from:
    • Android User generated screenshots: ✔️
    • Android System Generated screenshots: ✔️
    • iOS User generated screenshots: 🤷 (partial)
    • iOS System Generated screenshots: ❌

    Android

    import android.os.Bundle;
    import com.facebook.react.ReactActivity;
    import android.view.WindowManager;

    public class MainActivity extends ReactActivity {

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            getWindow().setFlags(
                WindowManager.LayoutParams.FLAG_SECURE,
                WindowManager.LayoutParams.FLAG_SECURE
            );
      }
    }


    iOS

    import React from 'react'
    import { AppState, Platform, View } from 'react-native'

    const SecurityScreen = () => <View />

    const showSecurityScreenFromAppState = appState =>
      ['background', 'inactive'].includes(appState)

    const withSecurityScreenIOS = Wrapped => {
      return class WithSecurityScreen extends React.Component {
        state = {
          showSecurityScreen: showSecurityScreenFromAppState(AppState.currentState)
        }

        componentDidMount () {
          AppState.addEventListener('change', this.onChangeAppState)
        }
      
        componentWillUnmount () {
          AppState.removeEventListener('change', this.onChangeAppState)
        }
      
        onChangeAppState = nextAppState => {
          const showSecurityScreen = showSecurityScreenFromAppState(nextAppState)

          this.setState({ showSecurityScreen })
        }  

        render() {
          return this.state.showSecurityScreen
            ? <SecurityScreen />
            : <Wrapped {...this.props} />
        }
      }
    }

    const withSecurityScreenAndroid = Wrapped => Wrapped

    export const withSecurityScreen = Platform.OS === 'ios'
      ? withSecurityScreenIOS
      : withSecurityScreenAndroid


    Then, in your App component:

    import { withSecurityScreen } from './withSecurityScreen'
    ...
    export default withSecurityScreen(App);


    Pre bundled libraries are also available on NPM [10].

    References: [9] [10]

    Cordova / Ionic

    There are many Cordova plugins which can help on achieving the goal. 
    A good place to start is the cordova-plugin-prevent-screenshot-coffice open source repository.

    Protects from:
    • Android User generated screenshots: ✔️
    • Android System Generated screenshots: ✔️
    • iOS User generated screenshots: 🤷 (partial)
    • iOS System Generated screenshots: ❌
    References: [11]

    Commercial Solutions

    Several commercial solutions are available on the market, however since there was no easy way to test any of them, this blog post will not promote or endorse any commercial tool.

    Conclusions - Wrapping it up

    TLDR; None of the proposed solutions will provide a full protection against screenshotting, therefore all of them should be considered as mitigations.

    Since all the OSs handle the application status transitions in a different way, and different versions of  OSs behave in a different way, it will always be possible to generate some sort of bypass of any implemented mitigation mechanism.
    Moreover, let's say that if an application is running on an untrusted - jailbroken or rooted - device  environment it is easy for an attacker to hook and bypass any kind of protection with some effort.

    The chosen mitigation should then be biased on which data the application is showing on the screen and how much sensitive is this data.
    Examples of such mitigation bypass are available in literature, such as [12].

    References


    Friday, February 26, 2021

    A Journey Into the Beauty of DNSRebinding - Part 1


    Authors

    Giovanni Guido
    Alessandro Braccio

    Abstract

    In this first blog post about DNS rebinding topic,  we are going to show a practical example of DNS Rebinding attack against UPnP services exposed in a local network. 

    The goal  of this post series is to show real attack scenarios against devices exposing services on a local network. 

    That case study tends to happen usually on IoT interconnected home or smart office devices, which are a very interesting scenario from an attacker perspective.

    Introduction

    Nowadays, IoT devices are around us in every environment, starting from our home in which smart devices interact with each other in order to simplify our daily tasks. These devices usually use different protocols in their communication, from common HTTP requests to Bluetooth Low Energy, but also pretty old protocols that are having a comeback such as UPnP ( Universal Plug and Play ).

    The main and most sensitive part of the services implemented by IoT smart devices are usually not directly exposed over the external network, therefore an attacker would need to gain a position within the local network in order to exploit them. 

    The purpose of this first post is to give an overview of the DNS Rebinding attack technique and to show a practical example involving common UPnP services implemented by typical home devices, such as routers.

    The DNS Rebinding Attack

    In short, DNS Rebinding is an attack technique that allows to bypass the Same Origin Policy (SOP), by exploiting the DNS cache of the browsers themselves.

    An attacker, after a first phase in which the victims are induced to visit a malicious web page, tricks the users' browser by using a controlled DNS server. 

    This attack is normally used to compromise devices present in a local network in order to use them as relay points, bypassing the local network NAT trust boundary. Below a scheme showing a more detailed example and the related steps:


    In particular:
    • - The attacker sets up a DNS service for a malicious domain
    • - The attacker tricks a victim into accessing the controlled malicious domain. This step could be done using phishing techniques, such as Cross-Site Scripting, Content Manipulation or Social Network and Instant Messaging spam. 
    • - Victim's browser, by visiting the malicious domain, will make a query for that domain's DNS settings. 
    • - The malicious DNS server responds to the query with the actual IP address of the domain. 
    Meanwhile, victim's browser caches the returned IP address. 
    • - Since the attacker has configured the DNS Time-To-Live (TTL) at the lowest possible value, when the cache retention time expires, the user's browser makes another DNS request for the same domain as it needs a new IP address
    • - The malicious DNS service, this time, responds with a private IP address, such as 192.168.1.1, related to a device present inside the target private network. 
    • - At this point, in victim's browser, all origin-based security policies enforced by the Same Origin Policy are potentially bypassed as the address related to the initial origin has been compromised. Therefore, any JavaScript code previously loaded from the malicious website is now able to access to any HTTP resource locally exposed in LAN by the target device pointing to 192.168.1.1.

    Now, supposing a Smart TV as the subject of the DNS Rebinding attack, which behavior can be considered exposed to such attacks?

    The Smart TV web server expects to receive requests with known and trusted values inside the HTTP “Host” header, for instance a legit request should contain “Host: 192.168.1.53”, where “192.168.1.53” is the IP address of the TV itself. A header such as “Host: attacker.mindedsecurity.com” should therefore raise a warning in the application.

    So, if the target device does not properly validate the “Host” header and it shows the same behavior with tampered values (i.e. the response is the same), this can be considered as likely exploitable,via DNS Rebinding attacks.

    The following sections describe a practical example of DNS Rebinding attack against a home router. 

    In this first example, it will be shown how to exploit the IGD Profile  from UPnP protocol implemented by some routers, in order to perform a NAT Injection attack. 

    However, it should be considered that this is just one of the possible attack scenarios and that DNS Rebinding attack possibilities are limitless especially in the IoT era. 

    The UPnP Protocol

    An interesting example of local service is UPnP, which is a pretty old protocol that is coming back from the past thanks to the growth of smart devices.

    UPnP is a protocol designed to support automatic device discovery within a network without any configurations from the users. This way, for instance, a smart TV application can expose a UPnP service in a local network (e.g. a home network) in order to give to the user the opportunity to control the video player from his smartphone or other devices.

    The UPnP stack includes different protocols such as TCP, UDP, HTTP and SOAP and can be summarized as follows:

    1. Discovery
    2. Description
    3. Control
    4. Eventing
    5. Presentation
    Some of these layers are briefly described below, but for more details the UPnP Device Architecture specification could be considered.

    Discovering UPnP devices & services inside a local network

    When a UPnP "client" (control point) is added to a local network, it starts looking for UPnP devices and services by using the SSDP (Simple Service Discovery Protocol) protocol: the control point performs a M-SEARCH HTTPU (HTTP over UDP) discovery request to a specific multicast address (239.255.255.250). All the listening devices will then reply with a unicast HTTPU response as shown in the following image.

    Below the example of a SSDP request used to discover all the UPnP devices in a network:

    Example of a SSDP response from a home router:


    There are a lot of UPnP discovery tools available in the wild.

    However, if you want to get your hands dirty in order to understand the UPnP protocol, just a few lines of code are necessary to perform the SSDP discovery.

    The SSDP response contains information related to the location of the Device Description resource, which is usually pointed by a URL inside the LOCATION response header.

    As shown below, the Device Description is an XML document that contains information related to the device itself, such as the name of the device or details related to the manufacturer, and the description of the services and actions exposed by the device.



    In short, this XML file contains all the information we need to know in order to identify the available services.

    The IGD Profile

    UPnP devices may implement custom profiles with custom services or use default ones. 
    A common profile used by many routers is the IGD profile, which includes a set of subprofiles related to the router configuration such as the “LANHostConfigManagement”, used for managing network configuration parameters, or the “WANIPConnection” which, as already described in the “Universal Plug and Play IGD A Fox in the Hen House" paper, exposes the "AddPortMapping" SOAP action that is particularly interesting from an attacker point of view.

    The “AddPortMapping” action is commonly used by other devices in the LAN to create new port mapping rules on the WAN interface of the router. 

    However, an attacker may exploit this action to inject arbitrary rows in the port forwarding table in order to forward the traffic to other internal/external clients or to expose internal services externally, such as the router web administration interface.

    As we saw from the discovery process, usually, UPnP services could be exploited if the attacker has access to the same local network of the target UPnP device.

    Here comes the DNS Rebinding technique to the rescue!

    Attacking Vulnerable UPnP services via DNS Rebinding Attack

    Therefore, it is now pretty clear why a router that implements the IGD Profile and does not perform the validation of the Host header is a perfect target for a DNS Rebinding attack.


    Below is reported the SSDP response of a router with the UPnP IGD profile enabled. The scan was performed using a custom go script that basically performs the SSDP discovery process, reads the location of the Device Description resource and parses it in order to list also the services and actions available for a detected UPnP device.


    The “AddPortMapping” action of the “WANConnectionDevice” service is regularly present in the implemented IGD Profile.

    Moreover, as shown in the HTTP request and response below, the router doesn't validate the Host header because it regularly returns a 200 response even if the value "attacker.mindedsecurity.com" is inserted within the Host header.

    Request:

    Response:

    The previous SOAP POST request, according to the AddPortMapping action specification, exposes the administration web interface running on 192.168.1.1:80 externally on port 8989, making then it accessible from Internet (if the NewRemoteHost is empty, its value is 0.0.0.0 by default).

    The requirements are satisfied and, therefore, we can perform a DNS Rebinding attack against the router and inject this custom rule into its port forwarding table. 

    The following HTML page was used as Proof of Concept of phishing page. The page was runned on localhost and the attacker domain in this case was 127-0-0-1.192-168-1-1.attacker.mindedsecurity.com.

    The JavaScript code embedded inside the page continuously performs the AddPortMapping SOAP POST request via XHR to http://127-0-0-1.192-168-1-1.attacker.mindedsecurity.com:5431


    The JavaScript code embedded in the HTML phishing page continuously performs the NAT Injection SOAP request via XHR, the attacker-controlled DNS server is hit multiple times and responds with the real IP address of the  127-0-0-1.192-168-1-1.attacker.mindedsecurity.com domain, which in this case is 127.0.0.1 because the web server was runned locally. 

    After a while, the DNS server returns the 192.168.1.1 IP address:


    This way, the request was successfully sent to the router adding the new port mapping rule:


    The following diagram shows the complete attack flow.

    Conclusion

    The described NAT Injection attack is just an example of how DNS Rebinding could be used to reach services running in a local network. It is however still possible to go deeper and use DNS Rebinding techniques in order to achieve Remote Code Execution.

    For this reason, in the next part it will be described as another practical example that will show how to achieve RCE starting from a vulnerable service running in a local network.

    References: