Say Cheese: Ransomware-ing a DSLR Camera

August 11, 2019

Research by: Eyal Itkin


Cameras. We take them to every important life event, we bring them on our vacations, and we store them in a protective case to keep them safe during transit. Cameras are more than just a tool or toy; we entrust them with our very memories, and so they are very important to us.

In this blog, we recount how we at Check Point Research went on a journey to test if hackers could hit us in this exact sweet spot. We asked: Could hackers take over our cameras, the guardians of our precious moments, and infect them with ransomware?

And the answer is: Yes.

Background: DSLR cameras aren’t your grandparents’ cameras, those enormous antique film contraptions you might find up in the attic. Today’s cameras are embedded digital devices that connect to our computers using USB, and the newest models even support WiFi. While USB and WiFi are used to import our pictures from the camera to our mobile phone or PC, they also expose our camera to its surrounding environment.

Our research shows how an attacker in close proximity (WiFi), or an attacker who already hijacked our PC (USB), can also propagate to and infect our beloved cameras with malware. Imagine how would you respond if attackers inject ransomware into both your computer and the camera, causing them to hold all of your pictures hostage unless you pay ransom.

Below is a Video Demonstration of this attack:

[embedded content]

Picture Transfer Protocol (PTP)

Modern DSLR cameras no longer use film to capture and later reproduce images. Instead, the International Imaging Industry Association devised a standardised protocol to transfer digital images from your camera to your computer. This protocol is called the Picture Transfer Protocol (PTP). Initially focused on image transfer, this protocol now contains dozens of different commands that support anything from taking a live picture to upgrading the camera’s firmware.

Although most users connect their camera to their PC using a USB cable, newer camera models now support WiFi. This means that what was once a PTP/USB protocol that was accessible only to the USB connected devices, is now also PTP/IP that is accessible to every WiFi-enabled device in close proximity.

In a previous talk named “Paparazzi over IP” (HITB 2013), Daniel Mende (ERNW) demonstrated all of the different network attacks that are possible for each network protocol that Canon’s EOS cameras supported at the time. At the end of his talk, Daniel discussed the PTP/IP network protocol, showing that an attacker could communicate with the camera by sniffing a specific GUID from the network, a GUID that was generated when the target’s computer got paired with the camera. As the PTP protocol offers a variety of commands, and is not authenticated or encrypted in any way, he demonstrated how he (mis)used the protocol’s functionality for spying over a victim.

In our research we aim to advance beyond the point of accessing and using the protocol’s functionality. Simulating attackers, we want to find implementation vulnerabilities in the protocol, hoping to leverage them in order to take over the camera. Such a Remote Code Execution (RCE) scenario will allow attackers to do whatever they want with the camera, and infecting it with Ransomware is only one of many options.

From an attacker’s perspective, the PTP layer looks like a great target:

  • PTP is an unauthenticated protocol that supports dozens of different complex commands.
  • Vulnerability in PTP can be equally exploited over USB and over WiFi.
  • The WiFi support makes our cameras more accessible to nearby attackers.

In this blog, we focus on the PTP as our attack vector, describing two potential avenues for attackers:

  • USB – For an attacker that took over your PC, and now wants to propagate into your camera.
  • WiFi – An attacker can place a rogue WiFi access point at a tourist attraction, to infect your camera.

In both cases, the attackers are going after your camera. If they’re successful, the chances are you’ll have to pay ransom to free up your beloved camera and picture files.

Introducing our target

We chose to focus on Canon’s EOS 80D DSLR camera for multiple reasons, including:

Magic Lantern (ML) is an open-source free software add-on that adds new features to the Canon EOS cameras. As a result, the ML community already studied parts of the firmware, and documented some of its APIs.

Attackers are profit-maximisers, they strive to get the maximum impact (profit) with minimal effort (cost). In this case, research on Canon cameras will have the highest impact for users, and will be the easiest to start, thanks to the existing documentation created by the ML community.

Obtaining the firmware

This is often the trickiest part of every embedded research. The first step is to check if there is a publicly available firmware update file in the vendor’s website. As expected, we found it after a short Google search. After downloading the file and extracting the archive, we had an unpleasant surprise. The file appears to be encrypted / compressed

The even byte distribution hints that the firmware is encrypted or compressed, and that whatever algorithm was used was probably a good one. Skimming through the file, we failed to find any useful pattern that could potentially be a hint of the existence of the assembly code for a bootloader. In many cases, the bootloader is uncompressed, and it contains the instructions needed for the decryption / decompression of the file.

Trying several decompression tools, such as Binwalk or 7Zip, produced no results, meaning that this is a proprietary compression scheme, or even an encryption. Encrypted firmware files are quite rare, due to the added costs of key management implications for the vendor.

Feeling stuck, we went back to Google, and checked what the internet has to say about this .FIR file. Here we can see the major benefit of studying a device with an extensive modding community, as ML also had to work around this limitation. And indeed, in their wiki, we found this page that describes the “update protection” of the firmware update files, as deployed in multiple versions over the years. Unfortunately for us, this confirms our initial guess: the firmware is AES encrypted.

Being open-source, we hoped that ML would somehow publish this encryption key, allowing us to decrypt the firmware on our own. Unfortunately, that turned out not to be the case. Not only does ML intentionally keep the encryption key secret, we couldn’t even find the key anywhere in the internet. Yet another dead end.

The next thing to check was if ML ported their software to our camera model, on the chance it contains debugging functionality that will help us dump the firmware. Although such a port has yet to be released, while reading through their forums and Wiki, we did find a breakthrough. ML developed something called Portable ROM Dumper. This is a custom firmware update file that once loaded, dumps the memory of the camera into the SD Card.

Using the instructions supplied in the forum, we successfully dumped the camera’s firmware and loaded it into our disassembler (IDA Pro). Now we can finally start looking for vulnerabilities in the camera.

Reversing the PTP layer

Finding the PTP layer was quite easy, due to the combination of two useful resources:

  • The PTP layer is command-based, and every command has a unique numeric opcode.
  • The firmware contains many indicative strings, which eases the task of reverse-engineering it.

Traversing back from the PTP OpenSession handler, we found the main function that registers all of the PTP handlers according to their opcodes. A quick check assured us that the strings in the firmware match the documentation we found online.

When looking on the registration function, we realized that the PTP layer is a promising attack surface. The function registers 148 different handlers, pointing to the fact that the vendor supports many proprietary commands. With almost 150 different commands implemented, the odds of finding a critical vulnerability in one of them is very high.

PTP Handler API

Each PTP command handler implements the same code API. The API makes use of the ptp_context object, an object that is partially documented thanks to ML. Figure 4 shows an example use case of the ptp_context:

As we can see, the context contains function pointers that are used for:

  • Querying about the size of the incoming message.
  • Receiving the incoming message.
  • Sending back the response after handling the message.

It turns out that most of the commands are relatively simple. They receive only a few numeric arguments, as the protocol supports up to 5 such arguments for every command. After scanning all of the supported commands, the list of 148 commands was quickly narrowed down to 38 commands that receive an input buffer. From an attacker’s viewpoint, we have full control of this input buffer, and therefore, we can start looking for vulnerabilities in this much smaller set of commands.

Luckily for us, the parsing code for each command uses plain C code and is quite straight-forward to analyze. Soon enough, we found our first vulnerability.

CVE-2019-5994 – Buffer Overflow in SendObjectInfo – 0x100C

PTP Command Name: SendObjectInfo
PTP Command Opcode: 0x100c

Internally, the protocol refers to supported files and images as “Objects”, and in this command the user updates the metadata of a given object. The handler contains a Buffer Overflow vulnerability when parsing what was supposed to be the Unicode filename of the object. Figure 5 shows a simplified code version of the vulnerable piece of code:

This is a Buffer Overflow inside a main global context. Without reversing the different fields in this context, the only direct implication we have is the Free-Where primitive that is located right after our copy. Our copy can modify the pKeywordsStringUnicode field into an arbitrary value, and later trigger a call to free it.

This looks like a good way to start our research, but we continued looking for a vulnerability that is easier to exploit.

CVE-2019-5998 – Buffer Overflow in NotifyBtStatus – 0x91F9

PTP Command Name: NotifyBtStatus
PTP Command Opcode: 0x91F9

Even though our camera model doesn’t support Bluetooth, some Bluetooth-related commands were apparently left behind, and are still accessible to attackers. In this case, we found a classic Stack-Based Buffer Overflow, as can be seen in Figure 6.

Exploiting this vulnerability will be easy, making it our prime target for exploitation. We would usually stop the code audit at this point, but as we are pretty close to the end of the handler’s list, let’s finish going over the rest.

CVE-2019-5999– Buffer Overflow in BLERequest – 0x914C

PTP Command Name: BLERequest
PTP Command Opcode: 0x914C

It looks like the Bluetooth commands are more vulnerable than the others, which may suggest a less experienced development team. This time we found a Heap-Based Buffer Overflow, as can be seen in Figure 7.

We now have 3 similar vulnerabilities:

  • Buffer Overflow over a global structure.
  • Buffer Overflow over the stack.
  • Buffer Overflow over the heap.

As mentioned previously, we will attempt to exploit the Stack-Based vulnerability, which will hopefully be the easiest.

Gaining Code Execution

We started by connecting the camera to our computer using a USB cable. We previously used the USB interface together with Canon’s “EOS Utility” software, and it seems natural to attempt to exploit it first over the USB transport layer. Searching for a PTP Python library, we found ptpy, which didn’t work straight out of the box, but still saved us important time in our setup.

Before writing a code execution exploit, we started with a small Proof-of-Concept (PoC) that will trigger each of the vulnerabilities we found, hopefully ending in the camera crashing. Figure 8 shows how the camera crashes, in what is described by the vendor as “Err 70.”

Now that we are sure that all of our vulnerabilities indeed work, it’s time to start the real exploit development.

Basic recap of our tools thus far: Our camera has no debugger or ML on it. The camera wasn’t opened yet, meaning we don’t have any hardware-based debugging interface. We don’t know anything about the address space of the firmware, except the code addresses we see in our disassembler. The bottom line is that we are connected to the camera using a USB cable, and we want to blindly exploit a Stack-Based buffer overflow. Let’s get started.

Our plan is to use the Sleep() function as a breakpoint, and test if we can see the device crash after a given number of seconds. This will confirm that we took over the execution flow and triggered the call to Sleep(). This all sounds good on paper, but the camera had other plans. Most of the time, the vulnerable task simply died without triggering a crash, thus causing the camera to hang. Needless to say, we can’t differentiate between a hang, and a sleep and then hang, making our breakpoint strategy quite pointless.

Originally, we wanted a way to know that the execution flow reached our controlled code. We therefore decided to flip our strategy. We found a code address that always triggers an Err 70 when reached. From now on, our breakpoint will be a call to that address. A crash means we hit our breakpoint, and “nothing”, a hang, means we didn’t reach it.

We gradually constructed our exploit until eventually we were able to execute our own assembly snippet – we now have code execution.

Loading Scout

Scout is my goto debugger. It is an instruction-based debugger that I developed during the FAX research, and that proved itself useful in this research as well. However, we usually use the basic TCP loader for Scout, which requires network connectivity. While we can use a file loader that will load Scout from the SD Card, we will later need the same network connectivity for Scout, so we might as well solve this issue now for them both.

After playing with the different settings in the camera, we realized that the WiFi can’t be used while the USB is connected, most likely because they are both meant to be used by the PTP layer, and there is no support for using them both at the same time. So we decided the time had come to move on from the USB to WiFi.

We can’t say that switching to the WiFi interface worked out of the box, but eventually we had a Python script that was able to send the same exploit script, this time over the air. Unfortunately, our script broke. After intensive examination, our best guess is that the camera crashes before we return back from the vulnerable function, effectively blocking the Stack-Based vulnerability. While we have no idea why it crashes, it seems that sending a notification about the Bluetooth status, when connecting over WiFi, simply confuses the camera. Especially when it doesn’t even support Bluetooth.

We went back to the drawing-board. We could try to exploit one of the other two vulnerabilities. However, one of them is also in the Bluetooth module, and it doesn’t look promising. Instead, we went over the list of the PTP command handlers again, and this time looked at each one more thoroughly. To our great relief, we found some more vulnerabilities.

CVE-2019-6000– Buffer Overflow in SendHostInfo – 0x91E4

PTP Command Name: SendHostInfo
PTP Command Opcode: 0x91E4

Looking at the vulnerable code, as seen in Figure 9, it was quite obvious why we missed the vulnerability at first glance.

This time the developers remembered to check that the message is the intended fixed size of 100 bytes. However, they forgot something crucial. Illegal packets will only be logged, but not dropped. After a quick check in our WiFi testing environment, we did see a crash. The logging function isn’t an assert, and it won’t stop our Stack-Based buffer overflow ????

Although this vulnerability is exactly what we were looking for, we once again decided to keep on looking for more, especially as this kind of vulnerability will most likely be found in more than a single command.

CVE-2019-6001– Buffer Overflow in SetAdapterBatteryReport – 0x91FD

PTP Command Name: SendAdapterBatteryReport
PTP Command Opcode: 0x91FD

Not only did we find another vulnerability with the same code pattern, this was the last command in the list, giving us a nice finish. Figure 10 shows a simplified version of the vulnerable PTP handler.

In this case, the stack buffer is rather small, so we will continue using the previous vulnerability.

Side Note: When testing this vulnerability in the WiFi setup, we found that it also crashes before the function returns. We were only able to exploit it over the USB connection.

Loading Scout – Second Attempt

Armed with our new vulnerability, we finished our exploit and successfully loaded Scout on the camera. We now have a network debugger, and we can start dumping memory addresses to help us during our reverse engineering process.

But, wait a minute, aren’t we done? Our goal was to show that the camera could be hijacked from both USB and WiFi using the Picture Transfer Protocol. While there were minor differences between the two transport layers, in the end the vulnerability we used worked in both cases, thus proving our point. However, taking over the camera was only the first step in the scenario we presented. Now it’s time to create some ransomware.

Time for some Crypto

Any proper ransomware needs cryptographic functions for encrypting the files that are stored on the device. If you recall, the firmware update process mentioned something about AES encryption. This looks like a good opportunity to finish all of our tasks in one go.

This reverse engineering task went much better that we thought it would; not only did we find the AES functions, we also found the verification and decryption keys for the firmware update process. Because AES is a symmetric cipher, the same keys can also be used for encrypting back a malicious firmware update and then signing it so it will pass the verification checks.

Instead of implementing all of the complicated cryptographic algorithms ourselves, we used Scout. We implemented a new instruction that simulates a firmware update process, and sends back the cryptographic signatures that the algorithm calculated. Using this instruction, we now know what are the correct signatures for each part in the firmware update file, effectively gaining a signing primitive by the camera itself.

Since we only have one camera, this was a tricky part. We want to test our own custom home-made firmware update file, but we don’t want to brick our camera. Luckily for us, in Figure 11 you can see our custom ROM Dumper, created by patching Magic Lantern’s ROM Dumper.

CVE-2019-5995 – Silent malicious firmware update:

There is a PTP command for remote firmware update, which requires zero user interaction. This means that even if all of the implementation vulnerabilities are patched, an attacker can still infect the camera using a malicious firmware update file.

Wrapping it up

After playing around with the firmware update process, we went back to finish our ransomware. The ransomware uses the same cryptographic functions as the firmware update process, and calls the same AES functions in the firmware. After encrypting all of the files on the SD Card, the ransomware displays the ransom message to the user.

Chaining everything together requires the attacker to first set-up a rogue WiFi Access Point. This can be easily achieved by first sniffing the network and then faking the AP to have the same name as the one the camera automatically attempts to connect. Once the attacker is within the same LAN as the camera, he can initiate the exploit.

Here is a video presentation of our exploit and ransomware.

[embedded content]

Disclosure Timeline

  • 31 March 2019 – Vulnerabilities were reported to Canon.
  • 14 May 2019 – Canon confirmed all of our vulnerabilities.
  • From this point onward, both parties worked together to patch the vulnerabilities.
  • 08 July 2019 – We verified and approved Canon’s patch.
  • 06 August 2019 – Canon published the patch as part of an official security advisory.

Canon’s Security Advisory

Here are the links to the official security advisory that was published by Canon:

We strongly recommend everyone to patch their affected cameras.


During our research we found multiple critical vulnerabilities in the Picture Transfer Protocol as implemented by Canon. Although the tested implementation contains many proprietary commands, the protocol is standardized, and is embedded in other cameras. Based on our results, we believe that similar vulnerabilities can be found in the PTP implementations of other vendors as well.

Our research shows that any “smart” device, in our case a DSLR camera, is susceptible to attacks. The combination of price, sensitive contents, and wide-spread consumer audience makes cameras a lucrative target for attackers.

A final note about the firmware encryption. Using Magic Lantern’s ROM Dumper, and later using the functions from the firmware itself, we were able to bypass both the encryption and verification. This is a classic example that obscurity does not equal security, especially when it took only a small amount of time to bypass these cryptographic layers.

SELECT code_execution FROM * USING SQLite;

August 10, 2019

Gaining code execution using a malicious SQLite database

Research By: Omer Gull


SQLite is one of the most deployed software in the world. However, from a security perspective, it has only been examined through the lens of WebSQL and browser exploitation. We believe that this is just the tip of the iceberg.

In our long term research, we experimented with the exploitation of memory corruption issues within SQLite without relying on any environment other than the SQL language. Using our innovative techniques of Query Hijacking and Query Oriented Programming, we proved it is possible to reliably exploit memory corruptions issues in the SQLite engine. We demonstrate these techniques a couple of real-world scenarios: pwning a password stealer backend server, and achieving iOS persistency with higher privileges.

We hope that by releasing our research and methodology, the security research community will be inspired to continue to examine SQLite in the countless scenarios where it is available. Given the fact that SQLite is practically built-in to every major OS, desktop or mobile, the landscape and opportunities are endless. Furthermore, many of the primitives presented here are not exclusive to SQLite and can be ported to other SQL engines. Welcome to the brave new world of using the familiar Structured Query Language for exploitation primitives.


This research started when omriher and I were looking at the leaked source code of some notorious password stealers. While there are plenty of password stealers out there (Azorult, Loki Bot, and Pony to name a few), their modus operandi is mostly the same:

A computer gets infected, and the malware either captures credentials as they are used or collects stored credentials maintained by various clients.
It is not uncommon for client software to use SQLite databases for such purposes.
After the malware collects these SQLite files, it sends them to its C2 server where they are parsed using PHP and stored in a collective database containing all of the stolen credentials.

Skimming through the leaked source code of such password stealers, we started speculating about the attack surface described above.
Can we leverage the load and query of an untrusted database to our advantage?
Such capabilities could have much bigger implications in countless scenarios, as SQLite is one of the most widely deployed pieces of software out there.

A surprisingly complex code base, available in almost any device imaginable. is all the motivation we needed, and so our journey began.

SQLite Intro

The chances are high that you are currently using SQLite, even if you are unaware of it.
To quote its authors:

SQLite is a C-language library that implements a small, fast, self-contained, high-reliability, full-featured, SQL database engine. SQLite is the most used database engine in the world. SQLite is built into all mobile phones and most computers and comes bundled inside countless other applications that people use every day.

Unlike most other SQL databases, SQLite does not have a separate server process. SQLite reads and writes directly to ordinary disk files. A complete SQL database with multiple tables, indices, triggers, and views is contained within a single disk file.

Attack Surface

The following snippet is a fairly generic example of a password stealer backend.

Given the fact that we control the database and its content, the attack surface available to us can be divided into two parts: The load and initial parsing of our database, and the SELECT query performed against it.

The initial loading done by sqlite3_open is actually a very limited surface; it is basically a lot of setup and configuration code for opening the database. Our surface is mainly the header parsing which is battle-tested against AFL.

Things get more interesting as we start querying the database.
Using SQLite authors’ words:

“The SELECT statement is the most complicated command in the SQL language.”

Although we have no control over the query itself (as it is hardcoded in our target), studying the SELECT process carefully will prove beneficial in our quest for exploitation.

As SQLite3 is a virtual machine, every SQL statement must first be compiled into a byte-code program using one of the sqlite3_prepare* routines.
Among other operations, the prepare function walks and expands all SELECT subqueries. Part of this process is verifying that all relevant objects (like tables or views) actually exist and locating them in the master schema.

sqlite_master and DDL

Every SQLite database has a sqlite_master table that defines the schema for the database and all of its objects (such as tables, views, indices, etc.).
The sqlite_master table is defined as:

The part that is of special interest to us is the sql column.
This field is the DDL (Data Definition Language) used to describe the object.
In a sense, the DDL commands are similar to C header files. DDL commands are used to define the structure, names, and types of the data containers within a database, just as a header file typically defines type definitions, structures, classes, and other data structures.

These DDL statements actually appear in plain-text if we inspect the database file:

During the query preparation, sqlite3LocateTable()  attempts to find the in-memory structure that describes the table we are interested in querying.

sqlite3LocateTable() reads the schema available in sqlite_master, and if this is the first time doing it, it also has a callback for every result that verifies the DDL statement is valid and build the necessary internal data structures that describe the object in question.

DDL Patching

Learning about this preparation process, we asked, can we simply replace the DDL that appears in plain-text within the file? If we could inject our own SQL to the file perhaps we can affect its behaviour.

Based on the code snippet above, it seems that DDL statements must begin with “create “.
With this limitation in mind, we needed to assess our surface.
Checking SQLite’s documentation revealed that these are the possible objects we can create:

The CREATE VIEW command gave us an interesting idea. To put it very simply, VIEWs are just pre-packaged SELECT statements. If we replace the table expected by the target software with a compatible VIEW, interesting opportunities reveal themselves.

Hijack Any Query

Imagine the following scenario:
The original database has a single TABLE called dummy that is defined as:

The target software queries it with the following:

We can actually hijack this query if we craft dummy as a VIEW:

This “trap” VIEW enables us to hijack the query – meaning we generate a completely new query that we totally control.

This nuance greatly expands our attack surface, from the very minimal parsing of the header and an uncontrollable query performed by the loading software, to the point where we can now interact with vast parts of the SQLite interpreter by patching the DDL and creating our own views with sub-queries.

Now that we can interact with the SQLite interpreter, our next question was what exploitation primitives are built into SQLite? Does it allow any system commands, reading from or writing to the filesystem?

As we are not the first to notice the huge SQLite potential from an exploitation perspective, it makes sense to review prior work done in the field. We started from the very basics.

SQL Injections

As researchers, it’s hard for us to even spell SQL without the “i”, so it seems like a reasonable place to start. After all, we want to familiarize ourselves with the internal primitives offered by SQLite. Are there any system commands? Can we load arbitrary libraries?

It seems that the most straightforward trick involves attaching a new database file and writing to it using something along the lines of:

We attach a new database, create a single table and insert a single line of text. The new database then creates a new file (as databases are files in SQLite) with our web shell inside it.
The very forgiving nature of the PHP interpreter parses our database until it reaches the PHP open tag of “<?”.
Writing a webshell is definitely a win in our password stealers scenario, however, as you recall, DDL cannot begin with “ATTACH”

Another relevant option is the load_extension function. While this function should allow us to load an arbitrary shared object, it is disabled by default.

Memory Corruptions In SQLite

Like any other software written in C, memory safety issues are definitely something to consider when assessing the security of SQLite.
In his great blog post, Michał Zalewski described how he fuzzed SQLite with AFL to achieve some impressive results: 22 bugs in just 30 minutes of fuzzing.

Interestingly, SQLite has since started using AFL as an integral part of their remarkable test suite.

These memory corruptions were all treated with the expected gravity (Richard Hip and his team deserve tons of respect). However, from an attacker’s perspective, these bugs would prove to be a difficult path to exploitation without a decent framework to leverage them.
Modern mitigations pose a major obstacle in exploiting memory corruption issues and attackers need to find a more flexible environment.

The Security Research community would soon find the perfect target!


Web SQL Database is a web page API for storing data in databases that can be queried using a variant of SQL through JavaScript.The W3C Web Applications Working Group ceased working on the specification in November 2010, citing a lack of independent implementations other than SQLite.

Currently, the API is still supported by Google Chrome, Opera and Safari.
All of them use SQLite as the backend of this API.

Untrusted input into SQLite, reachable from any website inside some of the most popular browsers, caught the security community’s attention and as a result, the number of vulnerabilities began to rise.
Suddenly, bugs in SQLite could be leveraged by the JavaScript interpreter to achieve reliable browser exploitation.

Several impressive research reports have been published:

A clear pattern in past WebSQL research reveals that a virtual table module named ”FTS” might be an interesting target for our research.


Full-Text Search  (FTS) is a virtual table module that allows textual searches on a set of documents.
From the perspective of an SQL statement, the virtual table object looks like any other table or view. But behind the scenes, queries on a virtual table invoke callback methods on shadow tables instead of the usual reading and writing on the database file.

Some virtual table implementations, like FTS, make use of real (non-virtual) database tables to store content.

For example, when a string is inserted into the FTS3 virtual table, some metadata must be generated to allow for an efficient textual search. This metadata is ultimately stored in real tables named “%_segdir” and “%_segments”, while the content itself is stored in “”%_content” where “%” is the name of the original virtual table.
These auxiliary real tables that contain data for a virtual table are called “shadow tables”

Due to their trusting nature, interfaces that pass data between shadow tables provide a fertile ground for bugs. CVE-2019-8457,- a new OOB read vulnerability we found in the RTREE virtual table module, demonstrates this well.

RTREE virtual tables, used for geographical indexing, are expected to begin with an integer column. Therefore, other RTREE interfaces expect the first column in an RTREE to be an integer. However, if we create a table where the first column is a string, as shown in the figure below, and pass it to the rtreenode() interface, an OOB read occurs.

Now that we can use query hijacking to gain control over a query, and know where to find vulnerabilities, it’s time to move on to exploit development.

SQLite Internals For Exploit Development

Previous publications on SQLite exploitation clearly show that there has always been a necessity for a wrapping environment, whether it is the PHP interpreter seen in this awesome blog post on abusing SQLite tokenizers or the more recent work on Web SQL from the comfort of a JavaScript interpreter.

As SQLite is pretty much everywhere, limiting its exploitation potential sounded like low-balling to us and we started exploring the use of SQLite internals for exploitation purposes.
The research community became pretty good at utilizing JavaScript for exploit development. Can we achieve similar primitives with SQL?

Bearing in mind that SQL is Turing complete ([1], [2]), we started creating a primitive wish-list for exploit development based on our pwning experience.
A modern exploit written purely in SQL has the following capabilities:

  • Memory leak.
  • Packing and unpacking of integers to 64-bit pointers.
  • Pointer arithmetics.
  • Crafting complex fake objects in memory.
  • Heap Spray.

One by one, we will tackle these primitives and implement them using nothing but SQL.

For the purpose of achieving RCE on PHP7, we will utilize the still unfixed 1-day of  CVE-2015-7036.

Wait, what? How come a 4-year-old bug has never been fixed? It is actually an interesting story and a great example of our argument.
This feature was only ever considered vulnerable in the context of a program that allows arbitrary SQL from an untrusted source (Web SQL), and so it was mitigated accordingly.
However, SQLite usage is so versatile that we can actually still trigger it in many scenarios ????

Exploitation Game-plan

CVE-2015-7036 is a very convenient bug to work with.
In a nutshell, the vulnerable fts3_tokenizer() function returns the tokenizer address when called with a single argument (like “simple”, “porter” or any other registered tokenizer).

When called with 2 arguments, fts3_tokenizer overrides the tokenizer address in the first argument with the address provided by a blob in the second argument.
After a certain tokenizer has been overridden, any new instance of the fts table that uses this tokenizer allows us to hijack the flow of the program.

Our exploitation game-plan:

  • Leak a tokenizer address
  • Compute the base address
  • Forge a fake tokenizer that will execute our malicious code
  • Override one of the tokenizers with our malicious tokenizer
  • Instantiate an fts3 table to trigger our malicious code

Now back to our exploit development.

Query Oriented Programming ©

We are proud to present our own unique approach for exploit development using the familiar structured query language. We share QOP with the community in the hope of encouraging researchers to pursue the endless possibilities of database engines exploitation.
Each of the following primitives is accompanied by an example from the sqlite3 shell.

While this will give you a hint of what want to achieve, keep in mind that our end goal is to plant all those primitives in the sqlite_master table and hijack the queries issued by the target software that loads and queries our malicious SQLite db file

Memory Leak – Binary

Mitigations such as ASLR definitely raised the bar for memory corruptions exploitation. A common way to defeat it is to learn something about the memory layout around us.
This is widely known as Memory Leak.
Memory leaks are their own sub-class of vulnerabilities, and each one has a slightly different setup.

In our case, the leak is the return of a BLOB by SQLite.
These BLOBs make a fine leak target as they sometimes hold memory pointers.

The vulnerable fts3_tokenizer() is called with a single argument and returns the memory address of the requested tokenizer. hex() makes it readable by humans.
We obviously get some memory address, but it is reversed due to little-endianity.
Surely we can flip it using some SQLite built-in string operations.

substr() seems to be a perfect fit! We can read little-endian BLOBs but this raises another question: how do we store things?

QOP Chain

Naturally, storing data in SQL requires an INSERT statement. Due to the hardened verification of sqlite_master, we can’t use INSERT as all of the statements must start with “CREATE “. Our approach to this challenge is to simply store our queries under a meaningful VIEW and chain them together.
The following example makes it a bit clearer:

This might not seem like a big difference, but as our chain gets more complicated, being able to use pseudo-variables will surely make our life easier.

Unpacking of 64-bit pointers

If you’ve ever done any pwning challenges, the concept of packing and unpacking of pointers should not be foreign.
This primitive should make it easy to convert our hexadecimal values (like the leak we just achieved) to integers. Doing so allows us to perform various calculations on these pointers in the next steps.

This query iterates a hexadecimal string char by char in a reversed fashion using substr().

A translation of this char is done using this clever trick with the minor adjustment of instr() which is 1-based.
All that is needed now is the proper shift that is on the right of the * sign.

Pointer arithmetics

Pointer arithmetics is a fairly easy task with integers at hand. For example, extracting the image base from our leaked tokenizer pointer is as easy as:

Packing of 64-bit pointers

After reading leaked pointers and manipulating them to our will, it makes sense to pack them back to their little-endian form so we can write them somewhere.
SQLite char()  should be of use here as its documentation states that it will “return a string composed of characters having the Unicode code point values of an integer.”
It proved to work fairly well, but only on a limited range of integers

Larger integers were translated to their 2-bytes code-points.
After banging our heads against SQLite documentation, we suddenly had a strange epiphany: our exploit is actually a database.
We can prepare beforehand a table that maps integers to their expected values.

Now our pointer packing query is the following:

Crafting complex fake objects in memory

Writing a single pointer is definitely useful, but still not enough. Many memory safety issues exploitation scenarios require the attackers to forge some object or structure in memory or even write a ROP chain.

Essentially, we will string several of the building blocks we presented earlier.
For example, let’s forge our own tokenizer, as explained here.
Our fake tokenizer should conform to the interface expected by SQLite defined here:

Using the methods described above and a simple JOIN query, we are able to fake the desired object quite easily.

Verifying the result in a low-level debugger, we see that indeed a fake tokenizer object was created.

Heap Spray

Now that we crafted our fake object, it is sometimes useful to spray the heap with it.
This should ideally be some repetitive form of the latter.

Unfortunately, SQLite does not implement the REPEAT() function like MySQL.
However, this thread gave us an elegant solution.

The zeroblob(N) function returns a BLOB consisting of N bytes while we use replace() to replace those zeros with our fake object.

Searching for those 0x41s shows we also achieved a perfect consistency. Notice the repetition every 0x20 bytes.

Memory Leak – Heap

Looking at our exploitation game plan, it seems like we are moving in the right direction.
We already know where the binary image is located, we were able to deduce where the necessary functions are, and spray the heap with our malicious tokenizer.

Now it’s time to override a tokenizer with one of our sprayed objects. However, as the heap address is also randomized, we don’t know where our spray is allocated.
A heap leak requires us to have another vulnerability.

Again, we will target a virtual table interface.
As virtual tables use underlying shadow tables, it is quite common for them to pass raw pointers between different SQL interfaces.

Note: This exact type of issue was mitigated in SQLite 3.20. Fortunately, PHP7 is compiled with an earlier version. In case of an updated version, CVE-2019-8457 could be used here as well.

To leak the heap address, we need to generate an fts3 table beforehand and abuse its MATCH interface.

Just as we saw in our first memory leak, the pointer is little-endian so it needs to be reversed. Fortunately, we already know how to do so using SUBSTR().
Now that we know our heap location, and can spray properly, we can finally override a tokenizer with our malicious tokenizer!

Putting It All Together

With all the desired exploitation primitives at hand, it’s time to go back to where we started: exploiting a password stealer C2.

As explained above, we need to set up a “trap” VIEW to kickstart our exploit. Therefore, we need to examine our target and prepare the right VIEW.

As seen in the snippet above, our target expects our db to have a table called Notes with a column called BodyRich inside it. To hijack this query, we created the following VIEW

After Notes is queried, 3 QOP Chains execute. Let’s analyze the first one of them.


Our first QOP chain should populate the heap with a large amount of our malicious tokenizer.

p64_simple_create, p64_simple_destroy, and p64_system are essentially all chains achieved with our leak and packing capabilities.

For example, p64_simple_create is constructed as:

As these chains get pretty complex, pretty fast, and are quite repetitive, we created makes things a bit simpler by generating these queries in pwntools style.
Creating the previous statements becomes as easy as:


[embedded content]


Now that we have established a framework to exploit any situation where the querier cannot be sure that the database is non-malicious, let’s explore another interesting use case for SQLite exploitation.

iOS Persistency

Persistency is hard to achieve on iOS as all executable files must be signed as part of Apple’s Secure Boot. Luckily for us, SQLite databases are not signed.

Utilizing our new capabilities, we will replace one of the commonly used databases with a malicious version. After the device reboots and our malicious database is queried, we gain code execution.

To demonstrate this concept, we replace the Contacts DB “AddressBook.sqlitedb”. As done in our PHP7 exploit, we create two extra DDL statements. One DDL statement overrides the default tokenizer “simple”, and the other DDL statement triggers the crash by trying to instantiate the overridden tokenizer. Now, all we have to do is re-write every table of the original database as a view that hijacks any query performed and redirect it toward our malicious DDL.

Replacing the contacts db with our malicious contacts db and rebooting results in the following iOS crashdump:

As expected, the contacts process crashed at 0x4141414141414149 where it expected to find the xCreate constructor of our false tokenizer.

Furthermore, the contacts db is actually shared among many processes. Contacts, Facetime, Springboard, WhatsApp, Telegram and XPCProxy are just some of the processes querying it. Some of these processes are more privileged than others. Once we proved that we can execute code in the context of the querying process, this technique also allows us to expand and elevate our privileges.

Our research and methodology have all been responsibly disclosed to Apple and were assigned the following CVEs:

  • CVE-2019-8600
  • CVE-2019-8598
  • CVE-2019-8602
  • CVE-2019-8577

Future Work

Given the fact that SQLite is practically built-in to almost any platform, we think that we’ve barely scratched the tip of the iceberg when it comes to its exploitation potential. We hope that the security community will take this innovative research and the tools released and push it even further. A couple of options we think might be interesting to pursue are

  • Creating more versatile exploits. This can be done by building exploits dynamically by choosing the relevant QOP gadgets from pre-made tables using functions such as sqlite_version() or sqlite_compileoption_used().
  • Achieving stronger exploitation primitives such as arbitrary R/W.
  • Look for other scenarios where the querier cannot verify the database trustworthiness.


We established that simply querying a database may not be as safe as you expect. Using our innovative techniques of Query Hijacking and Query Oriented Programming, we proved that memory corruption issues in SQLite can now be reliably exploited. As our permissions hierarchies become more segmented than ever, it is clear that we must rethink the boundaries of trusted/untrusted SQL input. To demonstrate these concepts, we achieved remote code execution on a password stealer backend running PHP7 and gained persistency with higher privileges on iOS. We believe that these are just a couple of use cases in the endless landscape of SQLite.

Check Point IPS Product Protects against this threat: “SQLite fts3_tokenizer Untrusted Pointer Remote Code Execution (CVE-2019-8602).”