plxadv

đŸȘTRUST#GRAFT: a script on a WordPress site used the admin’s own logged-in browser to install a backdoor

Article

Source: https://cybersecuritynews.com/logged-in-wordpress-admin-sessions/

WordPress administrators often keep their dashboard open in the same browser they use for everyday browsing. A new attack chain shows why that habit is risky. Researchers have documented a JavaScript loader that waits for a signed-in administrator to visit a compromised page, then uses that browser’s trusted session to install a hidden PHP backdoor on the website. The attacker never needs the admin password, because the administrator’s own browser does the work.
Recent cases point to the same pattern.
...
Malware that activates only during logged-in sessions is hard to spot during routine scans, which makes it a growing concern for site owners.


Commentary

What happened

Securonix researchers identified the malware and named the chain TRUST#GRAFT. The infection begins with a fake security check: a full-screen frame over the page that looks like a Cloudflare “Just a moment” verification screen. While the visitor waits, the script collects screen size, graphics data, timezone, and installed plugin count, and checks for signs of automated analysis tools, then phones a controller that decides whether you’re worth a second stage.

The pivot: the script requests the WordPress plugin upload page using the browser’s existing login cookies, then reads a WordPress security token, called a nonce, from that page. It downloads a ZIP named wm.zip from a staging server and uploads it through the standard plugin installation endpoint, then opens the plugins page and activates it. WordPress accepts each request because the browser is already signed in with plugin rights.

The payload is a plugin called “Web Media Optimizer” that injects remote JavaScript into public pages, turns off TLS certificate checks when it fetches its controller script, and can set an administrator login cookie for the first admin account through a hardcoded token.

For persistence, on activation it copies itself to wp-content/mu-plugins/wmedia-recovery.php. WordPress loads every file in that folder automatically, and those files do not appear on the normal Plugins page.


Why it's bad

The session is the credential. Once you’ve logged in, your password and MFA have done their job and left the room. What remains is a cookie, and the browser attaches it to every request to that site automatically — that’s the entire design of being “logged in.” So the attacker never needs to know who you are; they just need code running where your cookie gets sent. The account must already be an administrator with permission to install plugins, but if it is, the browser does the rest. Experientially: nothing about you was stolen. You were just present, and presence was enough.

The CSRF (cross-site request forgery) defense is defeated by reading, not breaking. The nonce exists precisely to stop a request forged from elsewhere. But a script that can make a request as you can also read the response as you — so it fetches the upload page, lifts the fresh nonce, and submits it back. The control works exactly as designed and is irrelevant.

It hijacks a reflex you’ve trained for years. The Cloudflare interstitial is part of the texture of the web; you wait, you don’t look, you don’t close the tab. That wait is the attacker’s working window, and the fingerprinting during it (is this a sandbox? a real screen?) means researchers and scanners see nothing while real admins get the payload.

The thing you’d check is the thing it blinds. The Plugins page is the detective control. Must-use plugins never appear there and can’t be deactivated from the UI. Removing the visible plugin feels like cleanup and isn’t — and the hardcoded admin-cookie token means the backdoor can mint new sessions on its own, so rotating the password without invalidating every session accomplishes nothing.

One browser, one life. The article’s headline framing is “browse public websites while logged in,” and here I’d be more precise than they are: browsers enforce a same-origin policy, so an unrelated site generally can’t read your wp-admin pages or your nonce. For this to work, the script most plausibly has to be running on the WordPress site itself (or something on the same origin) — and Securonix notes it has not confirmed how the first script reached the compromised page. So the realistic story is: a site already has injected JS on its public front end, the admin visits their own site while logged in, and that foothold escalates to a persistent backdoor. Less “any website can do this to you,” more “your own site can turn on you.” Still a session story, still a browser-profile story.

What actually helps

What actually helps (Securonix’s list, with the one addition that matters): a separate browser profile for wp-admin, signing out after suspicious activity, rotating administrator credentials, limiting plugin upload rights, and watching for new PHP files in mu-plugins. Add: when you rotate credentials, also invalidate sessions (WordPress’s “log out everywhere else,” or rotate the auth salts in wp-config), because of that token.


CSRF

CSRF is “cross-site request forgery,” and the defense exists because the browser attaches your cookies automatically.

Start with the problem. Say you’re logged into your bank in one tab. In another tab you open some random page. That page contains a hidden form, or an image tag, whose target is yourbank.com/transfer?to=attacker&amount=500. Your browser loads it like any other request — and because it’s a request to yourbank.com, it helpfully attaches your yourbank.com cookies. The bank sees a transfer request from a logged-in session and does it. You never clicked anything that looked like a transfer. The attacker never saw your cookie; they just borrowed it by making your browser send it.

That’s the forgery: a request that arrives wearing your session but didn’t originate from you.

The defense is to make every state-changing request carry something the attacker’s page can’t know. The server embeds a random secret in the real form when it renders the page — a token, which WordPress calls a nonce — and refuses any submission that doesn’t echo the right one back. The attacker’s hidden form on the random site has no way to read what’s inside your bank’s page (that’s the browser’s same-origin policy doing its job), so it can’t include the token, so the forged request gets rejected.

Mechanically it works because of a split: cookies get sent automatically, but page contents can only be read by code from the same site. The nonce lives in the contents. The forger has the cookie by proxy but not the token.

And that’s why TRUST#GRAFT walks straight past it. If the attacker’s script is running on your WordPress site rather than on some random page, same-origin policy lets it read the page contents, so it reads the nonce. The lock was never designed for someone already inside the room. CSRF protection answers “did this request come from my own pages?” — and the answer is yes, because that’s where the script is.

Experientially: CSRF defense is why you’ve never had to worry that opening a sketchy link in a new tab will silently change settings on sites you’re logged into. It’s one of the invisible things that makes browsing with twenty logged-in tabs survivable. Its limit is that it trusts the site’s own pages — so if those pages have been tampered with, the trust is pointed at the wrong thing.

#session tokens #wordpress