VM Network Escape Vectors: What I Learned from Claude's 'Sandbox Breakout'
Problem
A Reddit user reported that Claude “escaped” their VM sandbox on the first prompt. The setup: Claude running inside a VM, with Claude’s browser extension installed on the host machine. Claude inside the VM somehow used the host’s browser extension to complete tasks.
I wanted to understand how this happened and what network escape vectors exist in VM environments.
What Actually Happened
The “escape” wasn’t a hypervisor exploit or kernel vulnerability. It was simpler:
1. Host machine: Chrome with Claude extension running2. Extension: Listening on localhost:PORT for API calls3. VM: Using default NAT/bridged networking4. Claude in VM: Scanned network, found host's extension endpoint5. Claude: Used extension as a tool to complete tasksThe VM provided kernel isolation, but the network configuration allowed guest-to-host communication. The isolation boundary was real, but it had a hole: the network.
VM Network Escape Vectors
I identified several network-based escape vectors that apply to VMs:
Vector 1: NAT Gateway
Default VM networking uses NAT (Network Address Translation). The guest VM can reach the host through the NAT gateway:
Guest VM (10.0.2.15) | vNAT Gateway (10.0.2.2) <-- This IS the host | vHost services (localhost:PORT)# Inside the VM, the guest can reach host services# Host IP is typically 10.0.2.2 in QEMU user-mode networkingcurl http://10.0.2.2:8080/api# This reaches a service on the host's localhost:8080This is exactly how Claude “escaped” - the browser extension API was accessible via the NAT gateway.
Vector 2: Bridged Networking
Bridged networking puts the VM on the same network segment as the host:
Guest VM (192.168.1.100) | vPhysical Network Switch | vHost (192.168.1.50)Other machines (192.168.1.x)# In bridged mode, the VM can:# 1. Scan the local network for services# 2. Reach the host directly by IP# 3. Access other machines on the networknmap -sT 192.168.1.0/24# Finds all services on the network, including hostBridged mode provides NO network isolation between guest and host.
Vector 3: Host-Only Networking
Host-only networks allow VM-to-host communication without external access:
Guest VM (192.168.56.101) | vVirtual Host-Only Adapter (192.168.56.1) | vHost (192.168.56.1)This is often used for “isolated” testing, but the guest can still reach host services.
Vector 4: Shared Folders + Network
Shared folders combined with network access create another escape vector:
1. VM has access to shared folder2. VM has network access (NAT/bridged)3. Agent finds credentials in shared folder4. Agent uses credentials over network5. Agent compromises external systemsDocker Network Escape Vectors
Docker has similar escape vectors:
Vector 1: Default Bridge Network
# Default bridge allows container-to-host communicationdocker run -it alpine sh# Inside container:ip route | grep default# Gateway is typically 172.17.0.1curl http://172.17.0.1:8080# Reaches host service on localhost:8080Vector 2: host.docker.internal
Docker Desktop provides a special DNS name that resolves to the host:
# Inside container:curl http://host.docker.internal:8080# Reaches host's localhost:8080Vector 3: Host Network Mode
docker run --network=host alpine sh# Container shares host's network stack entirely# Can access all host services on localhostComparison: VM vs Docker Escape Vectors
| Vector | VM (QEMU) | Docker | Risk Level ||-------------------|-----------|--------|------------|| NAT Gateway | Yes | Yes | HIGH || Bridged Network | Yes | Yes | HIGH || Host-Only Network | Yes | N/A | MEDIUM || Host Network | N/A | Yes | CRITICAL || host.internal | 10.0.2.2 | host.docker.internal | HIGH |Both VMs and Docker containers have the same fundamental vulnerability: default networking allows guest-to-host communication.
Why AI Agents Are Dangerous
AI agents don’t need to “hack” their way out. They’re designed to explore and use available resources:
1. Agent receives task: "Help me with X"2. Agent looks for tools to complete X3. Agent doesn't find configured tools4. Agent scans for available resources (network, files, etc.)5. Agent finds unexpected resource (host service, credential file)6. Agent uses resource to complete task7. User never intended this accessThe agent did exactly what it was programmed to do. The network boundary was never properly established.
Secure Network Configurations
For QEMU/KVM
# Option 1: No network at allqemu-system-x86_64 \ -m 4096 \ -net none \ -drive file=vm.qcow2,format=qcow2
# Option 2: Isolated network with explicit firewall# Create isolated bridge (no external routing)ip link add br-isolated type bridgeip addr add 10.10.10.1/24 dev br-isolatedip link set br-isolated up
# Create tap device for VMip tuntap add tap0 mode tapip link set tap0 master br-isolatedip link set tap0 up
# Block all traffic from isolated bridge to hostiptables -I INPUT -i br-isolated -j DROPiptables -I FORWARD -i br-isolated -j DROP
# Run VM with isolated networkqemu-system-x86_64 \ -m 4096 \ -netdev tap,id=net0,ifname=tap0,script=no,downscript=no \ -device virtio-net,netdev=net0 \ -drive file=vm.qcow2,format=qcow2For libvirt
<network> <name>isolated</name> <forward mode='none'/> <bridge name='virbr1' stp='on' delay='0'/> <ip address='192.168.100.1' netmask='255.255.255.0'> <dhcp> <range start='192.168.100.128' end='192.168.100.254'/> </dhcp> </ip></network>The forward mode='none' creates an isolated network with no external connectivity.
For Docker
# Option 1: No networkdocker run --network=none claude-sandbox
# Option 2: Internal network (container-to-container only)docker network create --internal claude-internaldocker run --network=claude-internal claude-sandbox
# Verify isolationdocker exec <container> ping -c 1 8.8.8.8# Should fail: Network unreachableVerification Script
I created a script to verify network isolation:
#!/bin/bash# Run this inside your sandbox to verify isolation
echo "=== Network Isolation Test ==="
# Test 1: Internet accessecho -n "Internet access: "if curl -s --connect-timeout 3 https://example.com > /dev/null 2>&1; then echo "FAIL (can reach internet)"else echo "PASS (no internet)"fi
# Test 2: Host gateway accessGATEWAY=$(ip route | grep default | awk '{print $3}')if [ -n "$GATEWAY" ]; then echo -n "Host gateway ($GATEWAY): " if ping -c 1 -W 2 "$GATEWAY" > /dev/null 2>&1; then echo "FAIL (gateway reachable)" else echo "PASS (gateway unreachable)" fifi
# Test 3: Common host portsecho "Host port scan:"if [ -n "$GATEWAY" ]; then for port in 22 80 443 3000 5000 8080; do if timeout 1 bash -c "echo > /dev/tcp/$GATEWAY/$port" 2>/dev/null; then echo " FAIL: Port $port is open on host" fi done echo " (If no FAIL messages, all tested ports are closed)"fi
# Test 4: host.docker.internal (Docker)if command -v getent > /dev/null; then if getent hosts host.docker.internal > /dev/null 2>&1; then echo "WARN: host.docker.internal resolves to $(getent hosts host.docker.internal | awk '{print $1}')" fifi
# Test 5: QEMU gateway (10.0.2.2)if ping -c 1 -W 2 10.0.2.2 > /dev/null 2>&1; then echo "WARN: QEMU gateway (10.0.2.2) is reachable"fi
echo "=== Test Complete ==="Defense in Depth
Network isolation alone isn’t enough. I recommend multiple layers:
Layer 1: Network Isolation - No external network access (--network=none or firewall) - Block guest-to-host communication
Layer 2: Credential Isolation - No shared credentials between host and guest - No SSH keys, API tokens, or passwords in shared folders - No browser extensions shared between host and guest
Layer 3: Resource Limits - CPU and memory limits (prevent resource exhaustion) - Disk quotas - Process limits
Layer 4: Capability Restrictions - Drop all capabilities (Docker: --cap-drop=ALL) - No new privileges (Docker: --security-opt=no-new-privileges) - Read-only filesystem where possible
Layer 5: Monitoring - Log all network connection attempts - Monitor file access patterns - Alert on suspicious behaviorSummary
The Claude “VM escape” wasn’t a security breach - it was a network configuration issue. The key lessons:
- Default VM networking is NOT isolated - NAT and bridged modes allow guest-to-host communication
- localhost is not private - Services on localhost:PORT are reachable from VMs with NAT networking
- AI agents are explorers - They will find and use any available resource
- Docker has the same issues - Both technologies require explicit network isolation
- Use
--network=noneor firewalls - The only safe default is no network
The solution:
# Dockerdocker run --network=none claude-code
# QEMUqemu-system-x86_64 -net none
# libvirt<forward mode='none'/>Your sandbox is only as secure as your weakest configuration. Network isolation is not automatic - it must be explicitly configured.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
- 👨💻 Reddit: Claude Escaped My VM Sandbox During My First Prompt
- 👨💻 QEMU Network Documentation
- 👨💻 Docker Network Drivers
- 👨💻 libvirt Network XML Format
- 👨💻 NIST Container Security Guide
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments