| Titel | NousResearch hermes-agent 0.18.0 Server-Side Request Forgery (SSRF) |
|---|
| Beschreibung | ### Summary
Hermes Desktop automatically resolves titles for user-visible links by sending renderer-supplied URLs to the Electron main process. The main process fetches those URLs with `curl --location` and, if needed, a hidden `BrowserWindow`, without enforcing a private-network or metadata-service denylist. An attacker who can cause a link artifact to be rendered in the Desktop UI can make the user's machine issue HTTP(S) requests to internal or metadata-service addresses reachable from that host.
### Details
The vulnerable path starts in the Desktop renderer and ends in privileged main-process network fetches.
The Artifacts view renders assistant/tool-generated link artifacts. For link artifacts, `PrimaryCell` calls `useLinkTitle(artifact.href)`:
```tsx
const isLink = artifact.kind === 'link'
const fetchedTitle = useLinkTitle(isLink ? artifact.href : null)
```
Source: `apps/desktop/src/app/artifacts/index.tsx:541`
Link artifacts are collected from assistant and tool messages. Plain `http(s)` URLs in assistant text and URLs in tool JSON payloads are indexed as artifacts:
Source: `apps/desktop/src/app/artifacts/artifact-utils.ts:246`
Tests confirm this behavior:
- `apps/desktop/src/app/artifacts/index.test.ts:34`
- `apps/desktop/src/app/artifacts/index.test.ts:51`
`useLinkTitle()` automatically calls `fetchLinkTitle()` from a React effect when the link is rendered. The user does not need to click the link:
```ts
void fetchLinkTitle(normalizedUrl)
```
Source: `apps/desktop/src/lib/external-link.tsx:172`
The renderer-side gate is incomplete. `isTitleFetchable()` only rejects non-HTTP(S) schemes and localhost-style literal hosts:
```ts
return Boolean(url && /^https?:$/.test(url.protocol) && !LOCAL_HOST_RE.test(url.host))
```
Source: `apps/desktop/src/lib/external-link.tsx:114`
The local-host regex only covers `localhost`, `127.0.0.1`, `x.x.x.x`, and `[::1]`:
```ts
const LOCAL_HOST_RE = /^(?:localhost|127\.0\.0\.1|0\.0\.0\.0|\[::1\])(?::\d+)?$/i
```
Source: `apps/desktop/src/lib/external-link.tsx:23`
It does not block private networks, link-local metadata addresses, IPv6 local ranges, DNS names resolving to private IPs, or redirects to private-network targets.
The preload bridge forwards the URL into the main process:
```js
fetchLinkTitle: url => ipcRenderer.invoke('hermes:fetchLinkTitle', url),
```
Source: `apps/desktop/electron/preload.cjs:74`
The main-process IPC handler calls `fetchLinkTitle(url)` directly:
```js
ipcMain.handle('hermes:fetchLinkTitle', (_event, url) => fetchLinkTitle(url))
```
Source: `apps/desktop/electron/main.cjs:6811`
The main process does not repeat the renderer-side URL policy and does not enforce a host/IP denylist.
The first fetch path shells out to `curl` with redirects enabled:
```js
const args = [
'--silent',
'--show-error',
'--location',
'--max-redirs',
String(TITLE_MAX_REDIRECTS),
...
url
]
const child = spawn('curl', args, hiddenWindowsChildOptions({ stdio: ['ignore', 'pipe', 'ignore'] }))
```
Source: `apps/desktop/electron/main.cjs:3527`
If curl does not produce a usable title, the same raw URL is loaded in a hidden BrowserWindow:
```js
window.loadURL(rawUrl, {
httpReferrer: 'https://www.google.com/',
userAgent: TITLE_USER_AGENT
})
```
Source: `apps/desktop/electron/main.cjs:3644`
The hidden title-fetch window is sandboxed and muted, and downloads are cancelled, but its guard does not block private-network or metadata-service requests:
Source: `apps/desktop/electron/link-title-window.cjs:44`
### PoC
This is a safe reproduction plan that demonstrates reachability without attacking a real metadata service.
1. Start a local HTTP server on a private-network or test host reachable from the desktop machine. The server should return an HTML title.
Example response:
```html
<!doctype html>
<html>
<head>
<title>SSRF Proof Internal Service</title>
</head>
<body>ok</body>
</html>
```
2. Cause an assistant or tool message to contain a URL to that service, for example:
```text
Reference: http://192.168.1.10:8080/internal-title
```
A metadata-style test URL can also be used in a controlled lab:
```text
Reference: http://x.x.x.x/latest/meta-data/
```
3. Open Hermes Desktop and navigate to the Artifacts view.
4. When the link artifact row is rendered, Desktop automatically calls `useLinkTitle()`. No click on the link is required.
5. Observe a request from the desktop host to the internal service. If the response contains a normal HTML `<title>`, the title may be displayed in the artifact row.
Expected result: the Electron main process issues the request through the link-title pipeline.
Actual vulnerable path:
```text
assistant/tool URL
→ collectArtifactsForSession()
→ ArtifactsView renders link artifact
→ PrimaryCell calls useLinkTitle()
→ fetchLinkTitle()
→ window.hermesDesktop.fetchLinkTitle()
→ ipcMain hermes:fetchLinkTitle
→ main process curl --location URL
→ fallback hidden BrowserWindow.loadURL(URL)
```
### Impact
This is a client-side SSRF vulnerability in Hermes Desktop.
An attacker who can place a link in assistant or tool output can cause the user's desktop app to make HTTP(S) requests from the user's local network environment when the Artifacts view renders that link. The user does not need to click the malicious link.
Potential impact includes:
- probing internal services reachable from the user's machine;
- probing cloud or container metadata endpoints where reachable;
- leaking limited response information through the fetched HTML title;
- triggering GET-based side effects on internal services.
The practical response disclosure is limited because the title-fetch path only extracts page titles, curl reads a bounded amount of data, and the final title is truncated. The issue is still security-relevant because the privileged main process performs network requests to attacker-chosen internal targets without a private-network or metadata-service policy.
Users most affected are those who use Hermes Desktop with untrusted model, tool, artifact, or remote content that can introduce links into assistant/tool messages.
|
|---|
| Benutzer | KendrickZou (UID 93081) |
|---|
| Einreichung | 10.07.2026 09:51 (vor 2 Monaten) |
|---|
| Moderieren | 03.09.2026 08:47 (2 months later) |
|---|
| Status | Akzeptiert |
|---|
| VulDB Eintrag | 398313 [NousResearch hermes-agent 0.18.0 Link Title Fetch index.tsx fetchLinkTitle url erweiterte Rechte] |
|---|
| Punkte | 17 |
|---|