Vulnerability disclosure
SQL injection in the Zalktis accounting app via e-invoice imports
Zalktis accounting software (Windows)
CVE-2026-59109 is a SQL injection OffSeq found in Zalktis, a Latvian accounting application. When Zalktis imports a received electronic invoice, it builds its database lookups by pasting fields from the incoming document – the registration and VAT numbers, the EAN barcode and the unit-of-measure code – straight into the SQL text, with no parameterisation and no escaping. Several of those fields are set by whoever sends the invoice, so a single crafted invoice that a bookkeeper simply imports can read and alter the accounting database. Zalktis Programmas fixed it by parameterising the queries.
OffSeq
5 min read
Severity: HighCVSS 8.7CVE-2026-59109
Key facts
- Identifier
- CVE-2026-59109
- Class
- SQL injection (CWE-89, CWE-20)
- Severity
- High
- CVSS 4.0
- 8.7 (CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N)
- CVSS 3.1
- 8.8 (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H)
- Vendor
- Zalktis Programmas
- Affected
- Zalktis for Windows (< 2026.1.586; 2026.2 < 2026.2.592)
- Fixed in
- Zalktis 2026.1.586 and 2026.2.592
- Coordinated by
- CERT.LV
- Disclosed
- 2026
- Status
- Fixed; CVE assigned
- Role
- Reporter (finder)
§ 01. Overview
Zalktis is a Windows accounting application used by Latvian businesses. Like most accounting software in Latvia, it imports electronic invoices – UBL documents delivered over the national e-address / PEPPOL network – and e-commerce exports, turning each into ledger entries. Importing a received invoice is an everyday bookkeeping action.
OffSeq found that Zalktis built the SQL behind those imports by pasting fields taken straight from the incoming document into the query text. Several of those fields are controlled by whoever sends the invoice, so a crafted invoice can break out of a string literal and run attacker-chosen SQL inside the accounting database. No account, prior relationship or network position is needed – on the PEPPOL network anyone can address an invoice to the victim, and the only step the victim takes is importing it. Zalktis Programmas fixed the issue by parameterising the queries; it is tracked as CVE-2026-59109, coordinated by CERT.LV.
§ 02. Importing an e-invoice
When Zalktis imports a received e-invoice or an e-commerce export, it reads the document (ReadXml / XmlSerializer.Deserialize) and, for each party and line item, checks whether the referenced record already exists – a customer by registration or VAT number, a product by barcode, a unit of measure by its code. Those checks are validation routines such as KlientsNMREksiste, KlientsPVNnrEksiste and PreceEANEksiste.
Each routine issues a SELECT against the local VistaDB database. The problem is how the SELECT is built: the value being looked up is taken from the incoming document and concatenated directly into the SQL string, and the query runs on an unparameterised OleDbCommand / VistaDBDataAdapter. The application ships an escaping helper, Dazadi.sql_txt(), that strips single quotes – but it is never called on these import paths.
§ 03. Root cause: partner-controlled fields in the query text
Four fields carried from the incoming document reach a query unescaped:
| Field in the received document | Query built | Location |
|---|---|---|
Registration number (BuyerParty/SellerParty RegNumber) | SELECT PersonalID FROM Personal WHERE NMRNR = '<field>' | DatuParbaude.cs:320 |
VAT number (VATRegNumber) | ... WHERE PvnNr = '<field>' | DatuParbaude.cs:459 |
| EAN / barcode | ... WHERE Svitrkods = '<field>' | DatuParbaude.cs:763 |
Unit-of-measure code (<cbc:InvoicedQuantity unitCode="…">) | SELECT MervienibaLV FROM Units WHERE UneceCode = '<field>' | frmreksaraksti.cs:3381 |
Each sits inside a '…' string literal in the SQL. Because the sender controls the field and Dazadi.sql_txt() is not applied, a single quote in the field ends the literal and everything after it is parsed as SQL. The registration and VAT numbers and the barcode are attacker-chosen values in the invoice; the unit-of-measure code is an XML attribute on every line, so the unitCode path is reached automatically for each line of the invoice during import, with no extra action.
§ 04. Proof of concept
The finding was demonstrated with a non-destructive, boolean-based test that changes no data.
A valid UBL invoice carries a payload in the buyer's registration-number field:
When the invoice is imported, KlientsNMREksiste builds and runs:
The '1'='1' variant and the '1'='2 variant return different results – record found versus not found – which confirms the field is evaluated as SQL rather than treated as data. From the same entry point, a UNION extracts data:
The unit-of-measure code reaches the database with no user targeting at all – it fires for every invoice line on import:
which produces:
All testing was carried out in a controlled environment against systems OffSeq controls; no third-party systems were touched.
§ 05. Impact and severity
The published metrics are CVSS 4.0 base 8.7 (High) (CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N) and CVSS 3.1 base 8.8 (High) (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H). The vector is Network, low-complexity, no privileges: on the Latvian e-address / PEPPOL network any sender can deliver an invoice to the victim. The only interaction (UI:P / UI:R) is the victim importing it – a routine bookkeeping operation, not a security decision.
Successful injection gives full access to the accounting database: confidential data can be read and exfiltrated with UNION- and boolean-based extraction – customer records, credentials such as the Lietotajs.Parole values in the example, financial data – and records and payments can be altered. Because the vulnerable pattern is shared across roughly 20 import channels, it is not one isolated form: every installation that imports documents is affected.
§ 06. Remediation
The fix is to stop building queries from document text. The affected SELECTs must use parameters – bound values the database treats as data, never as SQL – instead of string concatenation, on every one of the shared import paths: the registration- and VAT-number and barcode lookups, the unit-of-measure lookup, and the roughly 20 channels that reuse the pattern.
Zalktis Programmas parameterised the affected queries and released fixed builds: 2026.1.586 for the branch preceding 1 July 2026, and 2026.2.592 for the branch released from 1 July 2026 onward. Users should upgrade to one of these builds or later. Until a fixed build is deployed, avoid importing e-invoices and e-commerce exports received from untrusted senders, and keep restorable backups of the accounting database taken before any import.
§ 07. Disclosure and outcome
The vulnerability was reported to CERT.LV through Latvia's national coordinated-vulnerability-disclosure platform on 30 June 2026, triaged and forwarded to the vendor on 1 July. Zalktis Programmas confirmed remediation by query parameterisation on 16 July 2026, in builds 2026.1.586 and 2026.2.592, and CVE-2026-59109 was reserved on 30 July 2026. The disclosure is coordinated by CERT.LV.
The discovery is credited to Nils Putniņš (OffSeq Cybersecurity).
§ 08. Disclosure timeline
- 2026-06-30OffSeq reports the SQL injection to CERT.LV through the national coordinated-disclosure platform
- 2026-07-01CERT.LV triages the report and forwards it to the vendor
- 2026-07-16Zalktis confirms the fix – the affected queries are parameterised – in builds 2026.1.586 and 2026.2.592
- 2026-07-30CVE-2026-59109 reserved
References
- CVE-2026-59109 – CVE Record (opens in a new tab) (https://www.cve.org/CVERecord?id=CVE-2026-59109)
- CERT.LV – coordinated vulnerability disclosure record (opens in a new tab) (https://cvd.cert.lv/disclosed/vuln-all-631428755)
- CVSS 4.0 calculator – this vector (8.7, High) (opens in a new tab) (https://www.first.org/cvss/calculator/4.0#CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N)
- Zalktis Programmas – vendor (opens in a new tab) (https://zalktis.lv/)
- CWE-89: Improper Neutralization of Special Elements used in an SQL Command (opens in a new tab) (https://cwe.mitre.org/data/definitions/89.html)
- CWE-20: Improper Input Validation (opens in a new tab) (https://cwe.mitre.org/data/definitions/20.html)
Credit
Discovered and reported by Nils Putniņš (OffSeq Cybersecurity); coordinated through CERT.LV and fixed by Zalktis Programmas in builds 2026.1.586 and 2026.2.592.
More results
Authorization bypass in 8x8’s JaaS SIP calling gateway
OffSeq found an authorisation bypass in the JaaS SIP gateway, the outbound-calling service behind 8x8's Jitsi as a Service platform. The invite endpoint decided whether a tenant may place outbound SIP calls by reading a feature flag out of the caller's own token – a value the tenant sets – rather than the tenant's provisioned entitlement. Any JaaS tenant without the paid SIP-outbound feature could therefore make 8x8's trunk dial an attacker-chosen destination, with an attacker-controlled caller ID presented from @8x8.vc. 8x8 fixed it over two rounds through their HackerOne program.
Vulnerability disclosure20267 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
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
