Welcome to this week’s edition of the Threat Source newsletter. “Experiment is the mother of knowledge.” ― Madeleine L'Engle, A Wrinkle in Time “Don't slide down the rabbit hole. The way down is a breeze, but climbing back's a battle.” ― Kate Morton, The Clockmaker's Daughter Hacker Summer Camp has come and gone, which means it’s time for you to start planning next year’s trip. I’m surely going to recap Camp Season, right? Nope. One of the things that I’ve really enjoyed lately is a segment on the Beers with Talos podcast that we call “Make Hazel a Hacker.” If you haven’t listened to it, this is a perfect time to start. Each episode we take a few minutes and pose a security question, term, or concept to Hazel and force her to come up with an idea or explanation on the spot. There are no parameters, so she’s faced with the entirety of information security — past, present, and future. I know, it’s insane. The craziest part is that (I think) Hazel came up with this idea and still volunteered to put herself in the line of fire. As we put Hazel’s feet to the fire, one of my favorite things happens: The rest of us listen in and offer our thoughts during her brainstorming process. Invariably, we’ve got three very different answers, ideas, hints, or directions for her. It’s surely maddening for Hazel, but to me, the best part of the discussion that inevitably follows is that although they’re all different, they’re all correct. For example, this past episode I asked her about a behavioral indicator (regarding “wallpaper.bmp”) that seems benign on its own, but can be interesting to use as a pivot for a threat hunt. We had various interesting angles to consider, backed by years of knowledge and experience. It gave us a good conversation, and that was a .bmp! One of the most nebulous things to learn in this field is that multiple things can be both different and correct. When you are making your decisions this week — whether it’s deciding on a new pivot in your hunting, what devices to prioritize in your patching and updating, or which books or online training to focus on — take a quick second and get a second, third, and fourth opinion. Then try something that’s outside of your normal wheelhouse but sounds good when it’s proposed. None of this is a solo sport. It’s a team game and the best plays come from a mix of perspectives, experiences, and mistakes. The “right” answer can wear many faces, and your ability to hold different truths will lead you to undiscovered territory, the rabbit hole where anomaly lives and breathes. So... welcome back from Vegas. Now go down a rabbit hole on a path you wouldn’t normally take because one of your friends (Joe) or your mortal enemy (Dave) told you that it would work. “She'd been to Narnia, Wonderland, Hogwarts, Dictionopolis. She had tessered, fallen through the rabbit hole, crossed the ice bridge into the unknown world beyond.” ― Anne Ursu, Breadcrumbs The one big thing Cisco Talos recently discovered "JWR," a previously undocumented, real-time phishing framework and likely variant of "The Outsider" phishing-as-a-service platform. JWR uses an open WebSocket connection that allows attackers to monitor keystrokes live and dynamically steer victims through fake checkout and login flows. Currently deployed via SMS lures impersonating regional toll and postal authorities, JWR enables operators to steal payment data, 2FA codes, identity documents, and device fingerprints. Why do I care? Because JWR is operator-driven in real time, attackers can actively bypass multi-factor authentication (MFA) by prompting victims for 2FA codes exactly when needed. The sheer volume of collected data gives threat actors a comprehensive identity profile primed for extensive follow-on fraud and network compromise. Furthermore, JWR's seamless integration with legitimate e-commerce platforms like Shopify makes these lures incredibly convincing to the untrained eye. So now what? Prioritize user education around SMS-based phish…
Starlette's HTTPEndpoint dispatches HTTP methods by lowercasing the method name and using getattr without restricting to known HTTP verbs. If an HTTPEndpoint subclass is registered via Route without specifying allowed methods, any HTTP method, including non-standard ones, can invoke attributes on the endpoint. This allows attackers to call internal methods not intended as HTTP handlers, potentially bypassing authorization checks. The issue affects Starlette versions from 0.4.3 up to but not including 0.5.5 and frameworks built on it like FastAPI. A fix is available in version 0.5.5 by explicitly specifying allowed HTTP methods in Route registrations.
Hub is a Node.js WebSocket server and client with added features. Prior to 0.2.16, every incoming unauthenticated WebSocket connection triggers loadDefaultConnectionEventListeners to call requestClientId, which calls rpc.send for the get-client-id action and pushes a request into RPC.requests. The RPC.waitForReply function starts a setInterval polling loop every 10 milliseconds that is cleared only after a matching reply; if the client remains silent and closes, the timer and pending request stay allocated because the socket close path does not cancel them. Repeated connections therefore cause unbounded timers and heap entries, exhausting CPU and memory and making the server unavailable. This issue is fixed in version 0.2.16.
OpenClaw CLI versions up to 2026.2.13 have a vulnerability in the Telnyx webhook handler of the optional voice-call plugin that allows unauthenticated HTTP POST requests if the Telnyx public key is not configured. This enables attackers to forge Telnyx events. The issue affects deployments where the voice-call plugin is enabled and the webhook endpoint is accessible to attackers. A fix is planned for version 2026.2.14 and later, which enforces signature verification by requiring the Telnyx public key configuration.
OpenClaw CLI versions up to 2026.2.1 have an authentication bypass vulnerability in the optional voice-call extension when using inbound allowlist or pairing policies. The flaw allows calls with empty or missing caller IDs and calls from numbers ending with an allowlisted number to bypass access controls. This could enable unauthorized callers to reach voice-call agents. The issue is fixed in version 2026.2.2 by rejecting calls with missing caller IDs and enforcing strict caller ID matching.
An incomplete fix for CVE-2026-32062 in the openclaw-cli package allows oversized WebSocket frames to be parsed before validation during voice calls. This resource-consumption vulnerability affects versions prior to 2026.3.31 and has a medium severity rating. The issue was addressed in version 2026.3.31.
A signature verification bypass vulnerability exists in the OpenClaw CLI's Twilio voice-call webhook extension when the ngrok free-tier loopback compatibility option is enabled. This optional voice-call extension is disabled by default, and the bypass only occurs if the specific configuration option 'tunnel.allowNgrokFreeTierLoopbackBypass' is explicitly enabled. Exploitation requires the webhook endpoint to be publicly reachable, typically via a public ngrok URL used during development. An attacker could send forged webhook requests without a valid Twilio signature, potentially causing unauthorized webhook event handling and request flooding. The issue affects OpenClaw CLI versions from 2026.1.29 up to but not including 2026.2.14. A fix is available in version 2026.2.14, which removes the signature verification bypass while preserving trusted forwarded headers for correct signature validation.
OpenClaw CLI versions before 2026.2.23 have a vulnerability where Twilio webhook replay events could bypass the voice-call manager's deduplication mechanism. This happens because normalized event IDs were randomized on each parse, allowing replayed events to be treated as new and potentially causing duplicate or stale call-state transitions. The issue is fixed in version 2026.2.23 by preserving provider event IDs during normalization, adding bounded replay deduplication in webhook validation, and enforcing per-call turn-token checks.
OpenClaw voice-call media-stream WebSocket connections accepted upgrades before validating streams, allowing unauthenticated pre-start sockets to remain open and increase resource usage. This vulnerability affects openclaw versions up to 2026.2.21-2 and @openclaw/voice-call versions up to 2026.2.21. The issue is patched in version 2026.2.22. The vulnerability can lead to resource exhaustion and degraded service availability under sustained abuse.
CVE-2026-41337 is a vulnerability in the openclaw-cli package where a voice-call Plivo replay can mutate the in-process callback origin before the replay is rejected. This issue affects versions from 2026.1.29 up to but not including 2026.3.31. The vulnerability has a medium severity with a CVSS score of 5.3. A fix is available in version 2026.3.31 and later.
OpenClaw CLI's Plivo V3 webhook replay key generation used the raw verification URL without sorting query parameters, allowing attackers to replay signed webhook events by reordering query parameters. This flaw was fixed in version 2026.3.28 by canonicalizing the replay key.
A vulnerability in the openclaw-cli package versions 2026.4.9 up to but not including 2026.4.10 allows the voice-call realtime WebSocket to accept oversized frames. This can lead to a remote availability impact by potentially disrupting service. The issue is fixed in version 2026.4.10 and later.
A vulnerability in the OpenClaw CLI Skill Workshop apply flow allows an agent tool call to override a pending approval state, applying changes prematurely. This affects versions from 2026.1.29 up to but not including 2026.5.6. The issue does not alter the trusted-operator model but could impact configurations where lower-trust inputs reach the affected path. The vulnerability has a medium severity with a CVSS score of 5.3.
A vulnerability in OpenClaw CLI allows skill-command dispatch to skip before-tool-call hooks, potentially bypassing hook-based auditing or policy enforcement. This affects versions from 2026.1.29 up to but not including 2026.5.6. The issue does not alter the trusted-operator model but may impact security depending on configuration and input trust levels. A patch is available in version 2026.5.6. Users are advised to avoid relying solely on hook enforcement for skill commands until patched and to apply general hardening measures such as restricting allowlists and disabling the affected feature if not needed.
n8n-mcp versions 2.14.1 up to but not including 2.47.13 running in HTTP transport mode log sensitive tool-call arguments including credential material such as bearer tokens, OAuth credentials, API keys, and webhook authentication headers. This logging occurs before any redaction, potentially exposing secrets in server logs accessible beyond the request trust boundary. The vulnerability requires authentication and is mitigated in stdio transport mode. A fix is available in version 2.47.13 that prevents logging sensitive values by summarizing arguments without recording raw data.
### Summary
`/api/block/getRefIDs` filters its results for reader roles through a helper that checks only the visibility tiers. The password tier is not checked, because the helper does not receive the request context and therefore cannot evaluate the publish auth cookie. A reader who has not entered a document's publish password learns that the document references a given block.
A function ten lines away in the same file does perform the full check, on the same input type.
### Details
**Route,** identical at `eef105683` (`kernel/api/router.go:235`) and dev `a7ae96ce` (`:245`):
```go
ginServer.Handle("POST", "/api/block/getRefIDs", model.CheckAuth, getRefIDs)
```
No `CheckReadonly`, no `CheckAdminRole`.
**The filter chain.** `getRefIDs` (`kernel/api/block.go:631`) checks `isEncryptedNotebookDeniedForPublish`, calls `model.GetBlockRefsInBox`, then for read-only roles:
```go
publishIgnore := model.GetInvisiblePublishAccess(publishAccess)
refDefs, originalRefBlockIDs = model.FilterRefDefsByPublishIgnore(publishIgnore, refDefs)
```
`FilterRefDefsByPublishIgnore` (`kernel/model/publish_access.go:1324`) collects the reference and definition identifiers, resolves their block trees, and delegates the decision to `FilterBlockTreesByPublishIgnore` (`:1314`), whose entire body is:
```go
for id, bt := range bts {
if CheckPathAccessableByPublishIgnore(bt.BoxID, bt.Path, publishIgnore) {
ret[id] = bt
}
}
```
Inside that helper, `CheckPublishAuthCookie`, `GetPathPasswordByPublishAccess`, `checkBlockTreeAccessableByPublishAccess` and `password` all appear zero times.
**The complete check exists in the same file.** `kernel/model/publish_access.go:431`:
```go
func checkBlockTreeAccessableByPublishAccess(c *gin.Context, publishAccess PublishAccess, bt *treenode.BlockTree) bool {
if bt == nil || IsEncryptedBoxDeniedByPublishAccess(bt.BoxID) {
return false
}
publishIgnore := filterDisablePublishAccess(publishAccess)
passwordID, password := GetPathPasswordByPublishAccess(bt.BoxID, bt.Path, publishAccess)
return CheckPathAccessableByPublishIgnore(bt.BoxID, bt.Path, publishIgnore) &&
(password == "" || CheckPublishAuthCookie(c, passwordID, password))
}
```
Same package, same file, same `*treenode.BlockTree` input. One takes the context and evaluates the password; the other does not receive it and structurally cannot.
**Route-level contrast.** The adjacent `getChildBlocks` and `getTailChildBlocks` both carry `model.CheckAdminRole`.
**Note on a prior assessment.** `getRefIDs` has been described as correctly filtered because it calls a publish-access filter. It does. The filter it calls covers the visibility tiers only.
### Proof of Concept
Kernel 3.7.2, publish mode on port 6808, `Publish.Auth.Enable` false, anonymous client. A public document referenced from a second document whose publish tier was changed between runs. `getRefIDs` was called anonymously each time.
| Tier of the referring document | Anonymous result |
|---|---|
| Public | reference returned (baseline) |
| Password-protected | **reference still returned** |
| Hidden | `[]`, filtered |
| Forbidden | `[]`, filtered |
The hidden and forbidden rows confirm the filter runs and works. The password-protected row is the defect.
### Impact
An anonymous reader in publish mode, or any publish `RoleReader`, learns that a password-protected document contains a reference to a given block, without entering that document's password, and receives the block identifiers involved.
Scoped precisely: the response carries identifiers only. `type RefDefs { RefID string; DefIDs []string }`, returned alongside `originalRefBlockIDs`, a map of identifier to identifier. There is no reference text, title or content. The disclosure is the existence of a relationship, plus identifiers usable as input to other endpoints.
Confidentiality only.
### Suggested fix
Thread `*gin.Context` into `FilterRefDefsByPublishIgnore` and `FilterBlockTreesByPublishIgnore`, and use `checkBlockTreeAccessableByPublishAccess` in place of the bare `CheckPathAccessableByPublishIgnore` call, so the password tier and the encrypted-box check are both applied.
Mojolicious versions from 4.59 up to but not including 9.48 for Perl expose a stable session CSRF token representation that can be exploited via a BREACH compression oracle attack. The vulnerability arises because the _csrf_token method generates and caches a single token per session, returning the same token on every call, which is placed in a hidden csrf_token input field. When responses containing this token also include attacker-controlled input and are gzip-compressed, an attacker can use compression length differences to recover the CSRF token and bypass csrf_protect validation. This vulnerability is critical and has a CVSS score of 9.1. A patch is available in version 3.36_3.