Skip to content
Last updated: May 08, 2023

Testing UIPasteboard


Static Analysis

The systemwide general pasteboard can be obtained by using generalPasteboard, search the source code or the compiled binary for this method. Using the systemwide general pasteboard should be avoided when dealing with sensitive data.

Custom pasteboards can be created with pasteboardWithName:create: or pasteboardWithUniqueName. Verify if custom pasteboards are set to be persistent as this is deprecated since iOS 10. A shared container should be used instead.

In addition, the following can be inspected:

  • Check if pasteboards are being removed with removePasteboardWithName:, which invalidates an app pasteboard, freeing up all resources used by it (no effect for the general pasteboard).
  • Check if there are excluded pasteboards, there should be a call to setItems:options: with the UIPasteboardOptionLocalOnly option.
  • Check if there are expiring pasteboards, there should be a call to setItems:options: with the UIPasteboardOptionExpirationDate option.
  • Check if the app swipes the pasteboard items when going to background or when terminating. This is done by some password manager apps trying to restrict sensitive data exposure.

Dynamic Analysis

Detect Pasteboard Usage

Hook or trace the following:

  • generalPasteboard for the system-wide general pasteboard.
  • pasteboardWithName:create: and pasteboardWithUniqueName for custom pasteboards.

Detect Persistent Pasteboard Usage

Hook or trace the deprecated setPersistent: method and verify if it's being called.

Monitoring and Inspecting Pasteboard Items

When monitoring the pasteboards, there is several details that may be dynamically retrieved:

  • Obtain pasteboard name by hooking pasteboardWithName:create: and inspecting its input parameters or pasteboardWithUniqueName and inspecting its return value.
  • Get the first available pasteboard item: e.g. for strings use string method. Or use any of the other methods for the standard data types.
  • Get the number of items with numberOfItems.
  • Check for existence of standard data types with the convenience methods, e.g. hasImages, hasStrings, hasURLs (starting in iOS 10).
  • Check for other data types (typically UTIs) with containsPasteboardTypes: inItemSet:. You may inspect for more concrete data types like, for example an picture as public.png and public.tiff (UTIs) or for custom data such as com.mycompany.myapp.mytype. Remember that, in this case, only those apps that declare knowledge of the type are able to understand the data written to the pasteboard. This is the same as we have seen in the "UIActivity Sharing" section. Retrieve them using itemSetWithPasteboardTypes: and setting the corresponding UTIs.
  • Check for excluded or expiring items by hooking setItems:options: and inspecting its options for UIPasteboardOptionLocalOnly or UIPasteboardOptionExpirationDate.

If only looking for strings you may want to use objection's command ios pasteboard monitor:

Hooks into the iOS UIPasteboard class and polls the generalPasteboard every 5 seconds for data. If new data is found, different from the previous poll, that data will be dumped to screen.

You may also build your own pasteboard monitor that monitors specific information as seen above.

For example, this script (inspired from the script behind objection's pasteboard monitor) reads the pasteboard items every 5 seconds, if there's something new it will print it:

const UIPasteboard = ObjC.classes.UIPasteboard;
    const Pasteboard = UIPasteboard.generalPasteboard();
    var items = "";
    var count = Pasteboard.changeCount().toString();

setInterval(function () {
      const currentCount = Pasteboard.changeCount().toString();
      const currentItems = Pasteboard.items().toString();

      if (currentCount === count) { return; }

      items = currentItems;
      count = currentCount;

      console.log('[* Pasteboard changed] count: ' + count +
      ' hasStrings: ' + Pasteboard.hasStrings().toString() +
      ' hasURLs: ' + Pasteboard.hasURLs().toString() +
      ' hasImages: ' + Pasteboard.hasImages().toString());

    }, 1000 * 5);

In the output we can see the following:

[* Pasteboard changed] count: 64 hasStrings: true hasURLs: false hasImages: false
        "public.utf8-plain-text" = hola;
[* Pasteboard changed] count: 65 hasStrings: true hasURLs: true hasImages: false
        "public.url" = "";
        "public.utf8-plain-text" = "";
[* Pasteboard changed] count: 66 hasStrings: false hasURLs: false hasImages: true
        "" = "<UIImage: 0x1c42b23c0> size {571, 264} orientation 0 scale 1.000000";
        "public.jpeg" = "<UIImage: 0x1c44a1260> size {571, 264} orientation 0 scale 1.000000";
        "public.png" = "<UIImage: 0x1c04aaaa0> size {571, 264} orientation 0 scale 1.000000";

You see that first a text was copied including the string "hola", after that a URL was copied and finally a picture was copied. Some of them are available via different UTIs. Other apps will consider these UTIs to allow pasting of this data or not.