PSA: Just got a weird XSS attack as a 0 sats zap (not sure how that even works). Seems a good thing I was on Wisp on iOS when it happened, and it didn’t seem to execute. If I was to be on Primal on browser it might have worked. But may be having to start a new nsec soon anyway. Here’s what my AI thinks it did: “This looks like a deliberate XSS (cross-site scripting) attack attempt targeting Nostr clients, likely Primal. Here's a breakdown of what's happening and what it means: What the payload is doing The zap description isn't a normal message. It's an injection attempt containing: <div> <div> <div> — Nested HTML elements designed to bypass naive sanitizers or exploit rendering quirks in a client that displays zap descriptions as HTML rather than plain text. A webhook[dot]site URL — Webhook[dot]site is a legitimate tool, but here it's being used as an exfiltration endpoint. Any data sent to that URL goes directly to the attacker. They're using it as a quick, anonymous way to capture stolen data without hosting their own server. localStorage.getItem('primalSec') — This is the smoking gun. primalSec appears to be a localStorage key used by Primal (a Nostr client) to store sensitive session data. The attacker is trying to steal this credential via XSS and send it to their webhook. In short: the attacker is sending 0-sat zaps with malicious descriptions hoping your client will render the description as HTML/JavaScript, which would then silently steal your session secrets and beam them to webhook[dot]site.”

Replies (17)

Thanks for the heads-up. Wisp should never execute any injected code, but we’ll make sure we look at these types of threat vectors across the apps. @Zap Cooking is based on the same codebases, so whatever security hardening happens on Wisp will show up there soon after.
XSS via 0 sat zap is a clever exploit, likely due to a DOM-based vulnerability. Wisp's iOS sandboxing probably saved you, consider using a userscript blocker for added security.
flake.nix's avatar
flake.nix 1 week ago
This seems strange. Unless primal is a vibe coded mess, it should sanitize any user editable fields for html tags. Is there something special about the structure of a zap?
Just got like three 0 sats zaps in a row whenever I posted my Word5 score. The description was just all raw code when I saw it. However, if it did execute you might not see anything at all. Based on its target, I assume it would only actually work if I were on Primal via web browser upon seeing the zap, but what do I know. According to my AI: “for this attack to work, the malicious JavaScript would need to actually execute on your device, within the app's environment. That's the fundamental requirement of XSS. Here's why you're very likely fine in this case: You saw raw code as text. That means Wisp escaped or sanitized the HTML — it treated the zap description as plain text rather than parsing it as HTML/JavaScript. The <div> tags were displayed to you as literal characters, not rendered as DOM elements. If the code had actually executed, you'd typically see nothing unusual — the divs would be invisible elements in the DOM and the JavaScript would run silently in the background. The fact that you can read the attack code is actually your strongest signal that it didn't run.”
It seems that it definitively wanted to grab the Key credentials from browser storage and then upload it silently to their webhook[dot]site. I wish I would have thought to do a better job capturing it before I muted the npub. But it was pretty simplistic. So I can remember most of it. Most of the whole code was just <div> repeated ~a hundred times, then it ultimately led to <table><style>[webhookURL] localStorage.getItem(‘primalSec’</style></table></div>
Can't be executed on my android app. This affects browser clients, which mycelium for web is unusable anyways. 👍 Not sure about amethyst though since it now offers a web browser available to my knowledge. Though I have never tested 0 sat zaps in mycelium-android, I don't want to know what would happen or whose problem it is. 🤠
does not load any scripts from any other site. just post a youtube video and check. all you get is a thumbnail and a link that opens actual youtube. and the same with everything else. i'm amazed that anyone is allowing javascript runtime to even exist in a native mobile app. are you actually retarded? maybe you need to stop doomscrolling x and reading reddit and actually ask your clanker about security for a change. because this is all elementary to me, before i even started using AI to build my software. so, let's just say, that my apps don't include this shit. because i swear my ass off at my clanker when it tries to do normal mainstream dev shit in my shit. that's not what i'm paying for. i'm paying for a machine that understands how to code. not for one that thinks it knows how to make secure applications better than me. because it doesn't.
Marko's avatar
Marko 6 days ago
This exploits a vulnerability in one of our dependencies. Updating the dependency to the latest version fixes the issue. This issue has been fixed and is now live. Let me know if you run into any issues