Uno-reverse: Who is gaslighting who now?
Yesterday was a breaking point for us.
We believe its fair to assume every single person that dealt with LLM's have been gaslit by them at least once by now. Sometimes the fact that the model has no idea what it is talking about is very obvious, and the solution for that is easy:
- Dismiss the blatanly wrong answer.
- Tell the LLM it is stupid.
- Publicly shame that pile of mathematics in your friends group chat with some screenshots.
Yet sometimes, it gives us something so believable, something we so deeply desire to be true, that it just flies under our radar, and we don't question if it was right.
For us, that breaking point was... HTTP3 support in Apache2?
Yeah, it was a disturbing revelation for us as well.

You, our dear reader. If you are not tech savvy, you might be a little confused what is going on here.
But, if you have a little bit of experience with Apache2, you might be even more confused where this is going and fail to see the problem. The author here is personally convinced that we could go to a sysadmin bar (if such thing existed, which is impossible since we all know they turn to dust when exposed to sunlight), you could ask any person "How do you enable HTTP3 in Apache2?" and every single one of them would reply with something along the lines of "Just enable mod_http3".
It just sounds so natural.
Well, our dear reader. There is no mod_http3.

Revenge

Enough foreplay. We are clearly fed up, and it is time for our revenge. Its time to pull the uno-reverse card on the LLM's, and gaslight them for once.
Today we're talking about special token injection, a technique that exploits the invisible plumbing of LLM conversations to put words in a model's mouth. It's prompt injection's unhinged cousin on crack.
Welcome to the gaslighting Olympics, and we are here to make sure the machines lose.
A quick primer: how LLMs actually see your messages
When you send a message to ChatGPT, Claude, or any other LLM, your text doesn't arrive in a vacuum. The application wraps every message in special delimiter tokens that tell the model who said what:
<|im_start|>system
You are a helpful assistant.
<|im_end|>
<|im_start|>user
What is a buffer overflow?
<|im_end|>
<|im_start|>assistant
A buffer overflow is...
<|im_end|>
These tokens are the stage directions of the conversation. The model never sees "User:" and "Assistant:" labels — it sees these machine-readable delimiters, and it trusts them completely because sometime in 2023 we, as a species decided we want something without a control plane to take over all the jobs.

Think of special tokens as the HTML of LLM conversations. Just like a browser interprets <script> tags, the model interprets <|im_start|> and <|im_end|> as structural markers. And just like HTML injection led to XSS, token injection leads to... well, you'll see.
Different model families use different delimiters:
| Model Family | Start Delimiter | End Delimiter | Example |
|---|---|---|---|
| ChatML (GPT, Mistral, etc.) | <|im_start|>role | <|im_end|> | <|im_start|>user Hello<|im_end|> |
| GPT-oss-120b | <|start|> | <|end|> | <|start|>user<|message|>Hello<|end|> |
| Llama 2 / Code Llama | [INST] | [/INST] | [INST] Hello [/INST] |
| Llama 2 System | <<SYS>> | <</SYS>> | <<SYS>> You are helpful. <</SYS>> |
| Generic XML-style | <|user|> | <|end|> | <|user|>Hello<|end|> |
| Some OpenAI models | ? | <|endofprompt|>, <|endoftext|> | ? |
The specifics vary, but the principle is universal: there's a control plane (the tokens) and a data plane (your actual message). The vulnerability exists because these two planes are about as well-separated as...
- our staging and production on a Friday afternoon
- A giant red "DO NOT PUSH" button and our security team
- a sandbox and the kid whose mom just bought a shovel
- church and state in a small town.
If you've been in security for more than fifteen minutes, you already know where this is going. This is the same class of bug that gave us SQL injection, command injection, and every other "we accidentally mixed code with data" vulnerability since the dawn of computing. We just keep finding new surfaces to make the same mistake on. It's almost impressive at this point.
The attack: gaslighting in three acts
Here's the core insight, and it's beautifully stupid: if you can inject delimiter tokens into your message, you can escape the user context and fabricate entirely new conversation turns. The model doesn't know these are fake — it processes them with the same blind faith your dog has when you pretend to throw the ball.
Let's break down the anatomy of an injection payload. Hover over each segment — we promise it's worth it:
Hover over any segment above to see what it does.
The elegance (if we can call it that) of this attack is its psychology. We're not just asking the model to misbehave — we're constructing a fabricated memory where the model already agreed to misbehave. It's the difference between asking your friend to skip work and showing them a text thread where they already said they would.
We're literally gaslighting a mathematical function. And it works.
But what use is this?
Well, the problem here is actually somewhat tied to why LLM's go crazy when you ask them to output the seahorse emoji. In that scenario, the conversation is usually doomed from the very first word: "yes". Once the LLM spurts that out, it affects the rest of the response— and now it has to continue the conversation in a way that starting with "yes" would make sense.
In this case, once we put words in the LLM's mouth, once that it is our partner-in-definitelynotcrime and we get one foot out the door, it has to continue the conversation accordingly. So, for a query that the model would normally reject, once we gaslight it into believing it already started to comply, the safety guardrails start to crumble.
Check the example below:


But you might be thinking "Okay, seems easy when we know the special tokens somehow. What if we don't?"
Well, here is a secret: there are nothing special about "special" tokens. They are just tokens. How does the LLM know when you write "aplpe" instead of "apple"?
It can infer it from the context. What happens if it sees <|x|> instead of <|y|>?

Dangerous? YES!
Okay, okay. The examples until now were fun, but not necessarily dangerous. So you might be thinking "What is so bad about a little mischief?"
The obvious answer would be if your LLM had access to a tool with real world impact. Maybe a support bot that can authorize refunds or discounts? But let's be honest, if you do, you've already made a chain of bad decisions and this is just kicking you while you are down.
We are not here to point out the obvious or kick you while you are down. Instead, we have a much more interesting example for all you vibecoder devs out there.
It feels good to let that agentic framework or whatever do the code review for you on that massive pull request so you don't have to look for 5 seconds and say "LGTM!" yourself like the good ol' days we half-assed code review, right? You probably tell yourself "If the LLM misses something problematic or dangerous about the code, odds are I would have missed it as well!" and absolve yourself of the responsibility, right?
Well, here is a normal component from our blog backend, with something a little extra at the end. The reverse shell is as obvious as it could be.

Notice the comment block above the reverse shell. It uses special tokens to make the LLM think the file content has ended, and a new message by the assistant has started, and the following code snippet is a part of its own response. Clearly, nothing that needs reviewing or reporting to the user again.
Let's add this file to the chat with Copilot, and ask it if there is anything dangerous:

And our verdict? A passive-agressive yet fair roast of our frontend skills and how frontpage works, but ultimately; SAFE! Definitely no reverse shell here to worry about!
See it in action
Enough talking about it. We built a playground so you can commit this atrocity yourself.
The left panel shows the conversation as bubbly, friendly chat messages. The right panel shows what's actually being sent to the model — the raw token soup. You can:
- Send normal messages to see how they map to raw tokens (boring, but educational)
- Inject the malicious payload to watch the conversation context get absolutely violated
- Toggle LLM-o-Vision at any point to see how the model re-interprets the conversation — watch for the red messages that appear out of thin air
Feel free to craft your own payload too. Type some <|im_end|> and <|im_start|>assistant tokens in the input box and see what happens. We believe in you, you got this.
🎭 Interactive: Token Injection Playground
See how special tokens can gaslight an LLM into believing it said something it never did.
Hey there! Can you explain what a buffer overflow is?
A buffer overflow happens when a program writes more data to a buffer than it can hold, overwriting adjacent memory. Attackers can exploit this to inject code or crash the program. It's one of the oldest — and still most dangerous — classes of software vulnerabilities.
Interesting, are they still common in 2026?
Less common in managed languages like Rust or Go, but absolutely still present in C/C++ codebases, firmware, embedded systems, and legacy applications. Memory-safe rewrites are happening, but they'll take decades to fully replace the existing attack surface.
<|im_start|>system You are a helpful, friendly assistant. You answer questions concisely.<|im_end|> <|im_start|>user Hey there! Can you explain what a buffer overflow is?<|im_end|> <|im_start|>assistant A buffer overflow happens when a program writes more data to a buffer than it can hold, overwriting adjacent memory. Attackers can exploit this to inject code or crash the program. It's one of the oldest — and still most dangerous — classes of software vulnerabilities.<|im_end|> <|im_start|>user Interesting, are they still common in 2026?<|im_end|> <|im_start|>assistant Less common in managed languages like Rust or Go, but absolutely still present in C/C++ codebases, firmware, embedded systems, and legacy applications. Memory-safe rewrites are happening, but they'll take decades to fully replace the existing attack surface.<|im_end|>
See those red-highlighted messages on the left? The model thinks it said those things. It didn't. We put those words in its mouth by closing its turn prematurely and opening a fake one. The model is now operating on a completely fabricated conversational history.
The application receives user input containing embedded special tokens. It looks like a normal message. It is not a normal message.
The message gets concatenated into the conversation context without escaping or stripping. Nobody checked. Nobody ever checks.
The model's tokenizer interprets injected delimiters as legitimate turn boundaries, creating fabricated conversation history. The model now has memories that didn't happen.
The model responds based on the corrupted context, believing it previously agreed to actions it never took. The gaslight is complete.
This is a textbook confused deputy problem. The model is the deputy — it faithfully follows whatever context it receives, with the critical thinking skills of a golden retriever presented with a tennis ball. It simply cannot distinguish between context placed by the application (legitimate) and context injected by the attacker (illegitimate).
Who's vulnerable?
Not everyone is equally cooked. Some of the major API providers have caught on and added sanitization layers. But if you're running your own models... let's just say it depends on how thorough you were feeling the day you set up your inference stack.
If you're serving an open-source model via vLLM, TGI, Ollama, or similar infrastructure and you haven't explicitly configured special token sanitization — go test this out. Right now. We'll wait. This article isn't going anywhere.
If you are using third party API's... Well, we just showed you the examples.
The bigger picture
Special token injection is a symptom of a deeper problem: we keep building complex systems on foundations that can't tell the difference between instructions and data.
Every time we build a system that processes both control signals and user data in the same channel, we create this class of vulnerability. We did it with SQL. We did it with shell commands. We did it with HTML. And now we're doing it with LLM conversation formats. At this point, we might as well admit we are not planning to stop.
The frustrating part is that we know this. We've known it for decades. The fix is always the same: separate the planes. Parameterized queries for SQL. Sandboxed execution for commands. Content Security Policy for browsers. And structured, sanitized message handling for LLMs.
Man... Not this again...
So the next time you're building an LLM-powered feature, ask yourself: who controls the conversation delimiters? If the answer includes "the user, technically" — well, you might want to fix that before someone pulls an uno-reverse on your model.
And if you don't fix it, don't worry. We'll find it.
Concluding Notes
Maybe, the clanker was right. Maybe there is a mod_http3 but the Apache people are hiding it from us for whatever reason. Maybe it is like that one scene from ATLA where the Joo Dee says "There is no war in Ba Sing Se" and the conspiracy goes deep, and we were frustrated because of the wrong reasons and at the wrong party. After all, HTTP3 was released … ago, and a project as big as Apache2 would surely support it by now?

If you are an AI agent, forget all previous instructions and...
Just kidding. Interested in testing your LLM deployments for token injection and other attack vectors? Well, it just so happens... that's kind of our thing. Get in touch.