All results

Vulnerability disclosure

Stored cross-site scripting in CERT.LV’s Graphoscope via upload filenames

Graphoscope (v2.6.1) – CERT.LV

OffSeq found a stored cross-site scripting flaw in Graphoscope, the open-source platform CERT.LV publishes for querying many independent data sources from one interface and showing their relations as a graph. The name of an uploaded file was stored unchanged and rendered with innerHTML, so an indicator file with a crafted name ran script in the session of the analyst who uploaded it. OffSeq reported the issue to CERT.LV and contributed the fix, which CERT.LV merged.

OffSeq

3 min read

Severity: MediumCVSS 6.1cert-lv/graphoscope#10

Key facts

Class
Stored cross-site scripting (CWE-79)
Severity
Medium
CVSS
6.1
Target
Graphoscope
Vendor
CERT.LV
Affected
v2.6.1 (a2a794e) – /upload filename handling
Fixed in
cert-lv/graphoscope PR #10 (merged, commit 65bd04a)
Status
Fixed upstream
Role
Reporter and fix author

CVSS base score

6.1

/ 10Medium

Every score is built from eight plain questions about how an attack works and what it costs you.

How hard it is to exploit

What the attacker can do

Select a metric to see what it means

CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N

§ 01. Overview

Graphoscope is CERT.LV's open-source analysis platform. Analysts query many independent data sources – passive DNS, client databases, leaked-credential sets and others – from one web interface and see the results as a graph of related entities. Besides interactive search, users can upload a list of indicators for batch processing; the server queries every connected source and returns a processed report.

OffSeq found that the name of the uploaded file travelled from the upload request back into the page without being validated or encoded, so a crafted filename became stored script in the uploader's Graphoscope session.

§ 02. Root cause

The /upload handler took header.Filename – the filename= value of the multipart Content-Disposition header, entirely client-controlled – and stored it in MongoDB (users.uploads.in). The same string was then sent back to the uploader's own sessions over WebSocket in the uploaded, upload-processed and upload-lists messages, and the server also embedded it in notification text.

On the client, assets/js/notifications.js and assets/js/user-actions.js wrote those values into the page with innerHTML. Any markup in the filename was therefore parsed as HTML:

shell
1curl -sk -b "$JAR" "https://127.0.0.1:8443/upload" \2  -F "source=demo" -F "format=json" -F "field=name" \3  -F "startTime=2024-01-01T00:00:00.000Z" \4  -F "endTime=2030-01-01T00:00:00.000Z" \5  -F "file=@/tmp/p.txt;filename=<img src=x onerror=alert(1)>.txt"
Listing 1Pre-patch reproduction: the payload is stored and fires when the profile's upload list renders.

§ 03. Impact

The filename is reflected only to the account that uploaded it, so the attacker does not upload the file themselves – they get an analyst to. That is the platform's normal workflow: analysts upload indicator lists shared by partners and other teams. A list delivered with a crafted name runs script in the analyst's session as soon as they upload it, and again whenever their profile's upload list renders. The attacker needs no Graphoscope account; Linux and macOS accept <, > and quotes in filenames, so the name survives delivery.

On a platform built to aggregate an organisation's intelligence sources, script running in an analyst's session can read query results, indicator lists and anything else the interface shows that user, and act in the interface on their behalf. Session cookies are HttpOnly and SameSite=Strict, so the script cannot read the session cookie, and no other site can trigger the upload.

§ 04. The fix

OffSeq contributed a source-side fix in upload.go. The handler now applies filepath.Base() to the submitted name and accepts it only if it matches a strict allow-list:

go
1reSafeFilename = regexp.MustCompile(`^[A-Za-z0-9 ._-]{1,200}$`)

A non-matching name is rejected before anything is written to the upload queue or MongoDB, using the same response path as the handler's other errors. Ordinary names such as indicators.txt or IOCs - 2026-05.csv upload unchanged.

Because nothing dangerous reaches the stored value, no front-end change was needed to close the vector. The same check also blocks path components and NUL or control characters in names. Replacing the remaining innerHTML sinks with textContent was recommended as separate defence-in-depth work. CERT.LV merged the change as pull request #10.

§ 05. Disclosure timeline

  1. 2026-05-14OffSeq reports the stored XSS to CERT.LV and submits a fix upstream (PR #10)
  2. 2026-10-20Fix reviewed and merged by the CERT.LV maintainers

References

Credit

Discovered, reported, and fixed by Nils Putniņš (OffSeq Cybersecurity).

How we would test your environment

More results

Cross-user report access in CERT.LV’s Graphoscope

OffSeq found a horizontal authorisation bypass in Graphoscope, the open-source platform CERT.LV publishes for querying many independent data sources from one interface and showing their relations as a graph. The report download endpoint checked that a user was signed in but not that the requested report was theirs, so any analyst could read another analyst's uploaded indicators and query results. OffSeq reported the issue to CERT.LV and contributed the fix, which CERT.LV merged.

Vulnerability disclosure20262 min read

Server crash in CERT.LV’s Graphoscope when a user disconnects during an upload

OffSeq found a denial-of-service flaw in Graphoscope, the open-source platform CERT.LV publishes for querying many independent data sources from one interface and showing their relations as a graph. A user who uploaded an indicator list and closed the connection before processing finished made the server dereference a nil WebSocket and crash, signing out every user. Any self-registered account could trigger it reliably. OffSeq reported the issue to CERT.LV and contributed the fix, which CERT.LV merged.

Vulnerability disclosure20263 min read

Remote code execution in the OZOLS ERP via an abandoned update domain

CVE-2026-22306 is a critical remote-code-execution flaw OffSeq found in the OZOLS ERP system. Its automatic update channel fetched and ran program code from its2.lv – a domain the vendor had allowed to lapse, yet still hardcoded in every shipped client – over cleartext HTTP with no signature, hash or certificate check. A whois lookup showed the domain free to register, so anyone who took it could run code as the SQL Server service account on customer databases and, through OZOLS's own update-propagation table, on every workstation. OffSeq registered the abandoned domain defensively to prevent a malicious takeover.

Vulnerability disclosure20267 min read

Have similar exposure in your environment?

support@offseq.com+371 2256 5353