Skip to main content

CVE-2025-65875: Untrusted Font Upload / Path Injection Can Lead to PHP Execution When Using FPDF

FPDF’s font definitions are PHP and are included at runtime; if an app allows attacker-controlled font definition paths/files, it can lead to code execution.

By Sanidhya Soni | February 01, 2026

Affected: FPDF ≤ 1.86

Vector: Remote code execution

Severity: High

Exploit prerequisites: The target application must allow an attacker to influence the file path passed to AddFont() (commonly via “custom font upload” features)


Summary

FPDF font definition files are PHP and are loaded using include(). If an application allows untrusted uploads to become font definition files, or allows attacker control of the path passed into AddFont(), the attacker can execute PHP. This is an insecure integration pattern, not a vulnerability in FPDF’s default behavior.


Background: how FPDF fonts are supposed to work

Instead of parsing .ttf files directly, FPDF uses a preprocessing step to generate PHP code that defines font metrics (like widths, heights, bounding box, etc.) pre-calculated in arrays.

When you add a font in FPDF, it executes a helper script to load these variables into memory.

This design creates a dangerous trust issue: If you incorrectly treat a user-uploaded .php as a font definition file, and that file happens to contain malicious code (like a web shell), it will be executed by the include() function.


Root cause

AddFont() forwards a path to _loadfont()

FPDF exposes AddFont() as the public entry point. In v1.86, this logic lives in fpdf.php around lines 443–461.

function AddFont($family, $style='', $file='', $dir='')
{
    if(strpos($file,'/')!==false || strpos($file,"\\")!==false)
        $this->Error('Incorrect font definition file name: '.$file);

    if($dir=='')
        $dir = $this->fontpath;

    if(substr($dir,-1)!='/' && substr($dir,-1)!='\\')
        $dir .= '/';

    $info = $this->_loadfont($dir.$file);  // Caller-supplied directory + file path; safe only if caller ensures it points to trusted font definition files.
}

_loadfont()

The core issue is FPDF loads font definition PHP via include(). It is safe only when loading trusted, developer-generated font definition files.

// Load a font definition file
protected function _loadfont($path)
{
    include($path); // EXECUTES ARBITRARY PHP CODE!
    if(!isset($name))
        $this->Error('Could not include font definition file: '.$path);
}

Intended FPDF font loading vs unsafe upload misuse


Exploitation scenario

FPDF alone is a library; exploitation depends on how it is integrated. A common pattern in SaaS/invoicing/reporting tools is a “custom branding” feature, where users can upload fonts for document generation.

Common unsafe integration patterns

  1. Custom font upload used directly by AddFont()

    The application accepts a “.php” upload and later calls AddFont() using the uploaded path.

  2. Attacker-influenced $dir / misconfigured fontpath

    Even when $file is constrained to a basename, a controllable $dir (or fontpath) can redirect font loading to an attacker-writable directory.

  3. Auto-loading fonts from a writable directory

    The application scans a fonts directory and calls AddFont() on discovered files, but that directory is writable by users/tenants.

A typical vulnerable chain will look like:

  1. The application provides a font upload feature (“upload .php”).

  2. The upload is stored on disk (e.g., uploads/fonts/<user>/foo.php).

  3. The application later calls:

    $pdf->AddFont('Foo', '', 'uploads/fonts/<user>/foo.php');
    
  4. FPDF calls _loadfont() which does include('uploads/.../foo.php').

If the stored file contains PHP code (e.g., <?php ... ?>), it will be executed.

Loaded file included by AddFont leads to RCE


Proof of Concept

Any file that reaches include() and contains PHP tags is sufficient. A minimal PoC could be:

<?php
// Required FPDF font definition variables
$name = 'EvilFont';
$type = 'TrueType';
$desc = array('Flags'=>32,'FontBBox'=>'[-665 -325 2000 1006]','ItalicAngle'=>0);
$up = -100;
$ut = 50;
$cw = array(chr(32)=>250,chr(65)=>722,chr(97)=>444);

//code execution
system("id; uname -a");

?>

Mitigation

Best addressed at the application boundary:

  1. Do not pass user-controlled paths to AddFont()
  2. If “custom fonts” are a hard requirement, Don’t feed uploads into AddFont() directly.
    • Generate the font definition yourself using trusted tooling (makefont.php) and store outputs in a controlled directory.
    • Enforce that only expected definition files (not arbitrary uploads) reach _loadfont().

Timeline

  • 2025-11-14 — Vulnerability discovered
  • 2025-11-15 — Reported to CVE Assignment Team
  • 2025-12-05 — CVE-2025-65875 assigned
  • 2026-02-01 — Public disclosure

References