My First Ever 0-Day: CVE-2026-4276

I found my first ever 0-day vulnerability, and it got assigned CVE-2026-4276. The target was LibreChat, an open-source AI chat platform, and the vulnerability itself is a log injection via CRLF injection in the RAG API.

For some context, I had just finished Global CPTC (Collegiate Penetration Testing Competition) and had some more free time on my hands. Prior to Globals, two of my buddies had found two 0-days in the same application at our regional competition for CPTC, which I was impressed by and figured if they’re able to find something on this application, then why can’t I? So I decided to take a closer look.


What is Log Injection?

Before I get into the details, let me quickly explain what log injection actually is. Basically, if an application writes user-controlled input into its log files without sanitizing it first, an attacker can inject fake log entries. This is done by inserting CRLF characters (\r\n) into the input, which the logging system interprets as new lines, allowing the attacker to craft whatever log entries they want.

This might sound harmless at first, but think about it from the perspective of someone working in a SOC. If you’re investigating a security incident and the logs you’re looking at have been tampered with, you have no idea which entries are real and which ones were planted by an attacker. You could be chasing phantom errors, looking at fake IP addresses, or following misleading timestamps while the actual attack is happening somewhere completely different.

On top of that, a lot of SIEMs have web-based UIs that render log data. If an attacker can inject arbitrary text into logs and those logs get displayed in a browser, it opens the door to things like XSS, SSTI, CSRF, and other client-side attacks. So what starts as a log injection can quickly escalate into other higher-impact vulnerabilities.


Finding the Vulnerability

I was (surprisingly) able to find this vulnerability the old-fashioned way: doing static code analysis using CodeQL and some default queries for Python. I ran the queries against LibreChat’s RAG API codebase, read through the CodeQL output, and one result immediately caught my eye. It flagged unsanitized user input being passed into a logging call. I decided to test it right away, and it worked on the first try.

The vulnerable endpoint is the /embed endpoint on the RAG API. When you send a POST request to this endpoint, one of the form data parameters is file_id. The application takes whatever value you put in file_id and logs it without doing any sanitization on it first.

So if you fire up Burp Suite, grab an authorization token from the LibreChat login page, and send a POST request to /embed, it looks something like this:

POST request to the embed endpoint


See that file_id field with unique-file-id-123? If you append CRLF characters (\r\n) after it, you can inject as many fake log lines as you want with whatever text you feel like putting in there. Here’s what the application logs look like after injecting some fake entries:

Injected log entries appearing in the application logs


Just like that, the logs are compromised. An attacker could inject fake error messages, bogus user IDs, spoofed IP addresses, or whatever else they need to cover their tracks or mislead an investigation.


The Vulnerable Code

The issue lives in app/routes/document_routes.py, specifically at line 770. The file_id value from the request is passed straight into a logging call with no sanitization at all.

Vulnerable code snippet


It’s one of those things that’s easy to miss during code review because on the surface it just looks like a normal log statement. But because the file_id comes directly from user input and there’s nothing stripping out control characters, an attacker gets full control over what ends up in the logs.


The Fix

The fix for this is pretty straightforward. You just need to strip out CRLF sequences from the file_id before it gets passed to the logger. That way, even if an attacker tries to inject newline characters, they get removed before the value ever hits the log file.

Remediation code


It’s a simple fix for a simple vulnerability, but the impact of leaving it unpatched is real. Log integrity matters, and once you lose trust in your logs, incident response becomes a nightmare.


Final Thoughts

Finding this was honestly a great learning experience. It’s not the flashiest vulnerability , but it reinforced something important: always sanitize user input, no matter where it ends up. Developers tend to think of sanitization in the context of SQL queries or HTML rendering, but logs are just as much of an attack surface.

I reported this to the LibreChat team and they were responsive about getting it patched. The whole process of finding the bug, writing the report, and getting a CVE assigned was really cool and I’m looking forward to hunting for more.