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

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

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

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

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

श्रेणियाँ

सभी श्रेणियाँ देखें
Loading categories
AfterLife — रिवोकेशन पर्सिस्टेंस डिटेक्शन लैब: जब पासवर्ड रीसेट सफल होता है लेकिन हमलावर कभी नहीं जाता। Strapi CVE-2026-22706 कंडीशनल-रिवोकेशन बग, उसका फिक्स, एक तीन-नियम डिटेक्शन पैक, और वह नैव नियम जो इसे मिस कर देता है, को रिप्रोड्यूस करता है। | Kitploit
उपकरण/GitHubGitHub/het-p301204/afterlife
रक्षात्मक उपकरणभेद्यता विश्लेषणवेब सुरक्षाप्रमाणीकरणलर्निंग और शिक्षारेड टीमिंगघटना प्रतिक्रियालैब और अभ्यास
GitHubhet-p301204/afterlife

AfterLife

रिवोकेशन पर्सिस्टेंस डिटेक्शन लैब: जब पासवर्ड रीसेट सफल होता है लेकिन हमलावर कभी नहीं जाता। Strapi CVE-2026-22706 कंडीशनल-रिवोकेशन बग, उसका फिक्स, एक तीन-नियम डिटेक्शन पैक, और वह नैव नियम जो इसे मिस कर देता है, को रिप्रोड्यूस करता है।

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

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

सभी देखें →

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

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

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

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

AFTERLIFE

निरसन दृढ़ता पहचान प्रयोगशाला

जब पासवर्ड रीसेट सफल हो जाता है लेकिन हमलावर कभी नहीं जाता।

एक स्थानीय red/blue प्रयोगशाला एक भेद्यता वर्ग के लिए: वह क्रेडेंशियल जो उस घटना से अधिक जीवित रहता है जिसे उसे समाप्त करना था। यह exploit, मूल कारण, समाधान, एक तीन-नियम पहचान पैक, उन चीज़ों के लिए एक स्थिति ऑडिट जो नियम संरचनात्मक रूप से नहीं देख सकते, एक फोरेंसिक कंसोल — और वह पहचान नियम जो काम नहीं करता, को रिपॉज़िटरी में रखा गया है ताकि उसे विफल होते हुए प्रदर्शित किया जा सके।

CI tests python rules OWASP CWE license

त्वरित शुरुआत · निष्कर्ष · सामान्य पहचान क्यों विफल होती है · नियम पैक · कंसोल · समाधान · मैट्रिक्स · परीक्षण · दस्तावेज़


दोनों कार्यान्वयनों के विरुद्ध वही हमला। प्रत्येक क्रेडेंशियल के लिए एक बार, जारी होने से मृत्यु तक, वंशावलियों में समूहित। धराशायी रेखा पासवर्ड परिवर्तन है। भेद्य मोड में पाँच बार इसे पार करते हैं और आगे बढ़ते रहते हैं; ठीक किए गए मोड में चोरी हुई वंशावली में प्रत्येक बार वहीं रुक जाता है।

वही हमला। वही अनुरोध। एक अंतर। scripts/figures.py द्वारा उसी पेलोड से उत्पन्न जिसे कंसोल बनाता है — CI में पुनः उत्पन्न और diff किया गया, ताकि कोई आकृति कोड से अलग न हो सके।


वह नियंत्रण कार्रवाई जो नियंत्रित नहीं करती```text

09:00 alice logs in ┐ refresh credential rt-001 │ the attacker steals rt-001 ┘

10:00 alice changes her password ← the one thing a victim can do alone HTTP 200 · password changed · fresh session issued

10:00:03 attacker: POST /refresh rt-002 → HTTP 200 new access credential at-004, issued 10:00:03

10:01:00 attacker: GET /me at-004 → HTTP 200 {"username": "alice", "authenticated": true}

root@kitploit:~
कोई त्रुटि नहीं। कोई विसंगति नहीं। गिनने के लिए कोई विफल प्रमाणीकरण नहीं। हमलावर का एक्सेस क्रेडेंशियल *तीन सेकंड पुराना* है और इसे सर्वर द्वारा, अनुरोध पर, रीसेट के बाद जारी किया गया था।

यहाँ पूरी भेद्यता है:```python
def _revoke_for_security_change(self, user, kind, device_id):
    if device_id:
        revoke_credentials(user, device_id=device_id)   # ← the finding

इसे एक समीक्षक की तरह पढ़ें। Revocation logic ठीक वहीं है। यह सही scope के साथ सही function को call करता है। इसके आस-पास का endpoint password hash को update करता है, 200 return करता है, और एक नया session issue करता है — एक सही password change का हर observable behaviour मौजूद है।

और अगर caller device_id छोड़ देता है, तो कुछ भी revoke नहीं होता, और endpoint फिर भी success report करता है।

यह कोई काल्पनिक स्थिति नहीं है। यह Strapi ≤ 5.33.2 में CVE-2026-22706 है, जहाँ refresh-token invalidation step caller-supplied deviceId पर conditional था। स्कोर 2.1, Low। इस पर नीचे चर्चा की गई है।


यह क्यों मायने रखता है

स्कोर इसलिए कम है क्योंकि attacker के पास पहले से ही access था — यही entry condition है, और यह bug कुछ नया नहीं देता। जो यह देता है वह है duration, और यह ऐसा उस एक control को तोड़कर करता है जिसे victim स्वयं operate कर सकता है।

  • हर account-takeover runbook reset the password से शुरू होता है। हर product उपयोगकर्ता को यही बात बताता है।
  • जब यह चुपचाप fail होता है, तो victim को बताया जाता है कि समस्या हल हो गई है और वह देखना बंद कर देता है — जिससे वह detection signal बंद हो जाता है जो account takeover में सबसे ज़्यादा मायने रखता है: उपयोगकर्ता का ध्यान देना।
  • Persistence window refresh credential के lifetime के बराबर है: डिफ़ॉल्ट रूप से 30 दिन, rotating के ज़रिए अनिश्चित काल तक renewable। test_persistence_lasts_as_long_as_the_refresh_credential इसे दिखाने के लिए सात दिनों का simulated time चलाता है।
  • उपयोगकर्ता के पास कोई दूसरी कार्रवाई नहीं है। दूसरा password change ठीक उतना ही करता है जितना पहला।

Containment path में Low-scored bug की कीमत feature path में Low-scored bug से ज़्यादा होती है, क्योंकि कीमत incident के दौरान चुकाई जाती है, जब कोई advisories नहीं पढ़ रहा होता।


The naive detector

जो rule आप पहले लिखते हैं:```text IF credential.issued_at < credential_change.timestamp: ALERT

root@kitploit:~
यह कोई बेवकूफ़ी भरा नियम नहीं है। यह सस्ता है, एक फ़ील्ड चाहिए, समस्या की परिभाषा जैसा पढ़ा जाता है, और यह **साधारण मामले के बारे में सही है** — रीसेट के बाद चोरी हुए *access* टोकन का उपयोग करने वाला हमलावर पकड़ा जाता है।
`test_the_naive_rule_catches_the_simple_case` यह दावा करता है कि यह काम करता है।

दोनों नियम एक ही क्रेडेंशियल के बारे में एक ही सवाल पूछते हैं। वे जवाब देने के लिए अलग-अलग फ़ील्ड पढ़ते हैं, और दोनों फ़ील्ड असहमत हैं:

![दो पंक्तियाँ, प्रति फ़ील्ड एक। credential.issued_at 10:00:03 पढ़ता है, बदलाव के तीन सेकंड बाद, और निष्कर्ष निकालता है कि क्रेडेंशियल साफ़ दिखता है। lineage.root_issued_at 09:00:00 पढ़ता है, बदलाव से एक घंटा पहले, और अलर्ट करता है।](https://raw.githubusercontent.com/het-p301204/afterlife/main/docs/figures/two-fields.svg)

**तीन सेकंड बाद, या एक घंटा पहले — एक ही क्रेडेंशियल, एक ही क्षण में।** naive नियम हमलावर के नियंत्रण वाला फ़ील्ड पूछता है, और एक `POST /refresh` उसे रीसेट कर देता है।

इसे request log पर चलाएँ, जहाँ यह क्वेरी वास्तव में लिखी जाती है, क्योंकि request logs ही आपके पास होते हैं:```text
NAIVE DETECTOR (NAIVE-001), over the request log

  Result: NO ALERT

  Six requests were served to the attacker after the reset. Every one of them
  carried at-004, minted at 10:00:03 -- three seconds *after* the password
  change. By its own timestamp it is the newest credential on the account.

यहीं रुक जाना आसान होता, और बेईमानी भी, इसलिए लैब naive नियम का सबसे निष्पक्ष संस्करण भी चलाती है — जिसे refresh endpoint को भी देखने के लिए विस्तारित किया गया है:```text NAIVE DETECTOR, widened to include POST /refresh

1 alert at 2026-09-11T10:00:03.000Z: the credential presented to /refresh was rt-002, issued 2026-09-11T09:30:00.000Z. Then it goes blind. 6 events follow that hop and it flags none of them, because every credential from there on carries a post-reset timestamp. Its incident covers 1 accepted request; the lineage rule's covers all of them.

And look at what the alert names: NAIVE-001 revoke rt-002 -- rotated away and already dead at 10:00:03 AFTERLIFE-001 revoke lin-001 -- the live thing every future credential descends from

root@kitploit:~
यही अंतर है जो रात 3 बजे मायने रखता है। भोला नियम उस एक क्षण को पकड़ लेता है जब श्रृंखला सीमा पार करती है, फिर शेष 29 दिनों तक निशान खो देता है — और जिस क्रेडेंशियल का वह नाम लेता है वह *पहले ही घुमाया जा चुका है और सर्वर द्वारा निरस्त कर दिया गया है*। उसे निरस्त करने से कुछ हासिल नहीं होता। `lin-001` वह वस्तु है जिसे आपको खत्म करना है।

**और भोला नियम टेलीमेट्री से वंचित नहीं था।** यह उसी इवेंट स्ट्रीम, उसी वंशावली इंडेक्स, उसी सहनशीलता, उसी डुप्लिकेशन-हटाने और उसी सीमित स्थिति का उपयोग करता है। यह एक विधि को ओवरराइड करता है:```python
class Correlator:                                  # AFTERLIFE-001
    def _age_reference(self, facts):
        return facts.root_issued_at

class NaiveCorrelator(Correlator):                 # NAIVE-001
    def _age_reference(self, facts):
        return facts.issued_at

root_issued_at उस इंडेक्स में मौजूद है जिसे यह पहले से देख रहा है। test_the_naive_rule_had_the_data_it_needed इसे साबित करता है। विफलता तुलना में है, लॉगिंग में नहीं।


नियम पैक

एक लॉग पर तीन नियम। वे अलग-अलग सवालों के जवाब देते हैं, और वे उसी क्रम में सक्रिय होते हैं जिस क्रम में कोई घटना वास्तव में सामने आती है।


10:00:00 HIGH AFTERLIFE-003 Incomplete revocation at a security change 10:00:03 HIGH AFTERLIFE-001 Post-revocation credential lineage use

root@kitploit:~
**यह क्रम इस रिपॉज़िटरी की सबसे उपयोगी चीज़ है।** AFTERLIFE-003
उसी क्षण सक्रिय होता है जब परिवर्तन लागू होता है, हमलावर के कुछ भी छूने से तीन सेकंड पहले, क्योंकि इसके लिए आवश्यक साक्ष्य पहले से ही पूर्ण होते हैं: लॉग बताता है कि कौन-कौन से वंश (lineages) प्रवेश के समय सक्रिय थे, और यह नहीं बताता कि उन्हें रद्द (revoked) किया गया था।

इसे किसी पीड़ित या शोषण की आवश्यकता नहीं है। यह किसी भी उपयोगकर्ता द्वारा किए गए पहले पासवर्ड रीसेट पर ही दोष की रिपोर्ट कर देगा — जो इसे वह बनाता है जिसे आप स्टेजिंग में चलाते हैं, जहाँ प्रतीक्षा करने के लिए कोई हमलावर नहीं होता। AFTERLIFE-001 आपको बताता है कि उल्लंघन (breach) जारी है; AFTERLIFE-003 आपको बताता है कि आपका कंटेनमेंट नियंत्रण टूटा हुआ है।

![दो अलर्ट कार्ड। AFTERLIFE-003 10:00:00 पर verdict no_containment, watermark not recorded, scope none, और lin-001 surviving की रिपोर्ट करता है। AFTERLIFE-001 10:00:03 पर credential rt-002 की रिपोर्ट करता है जिसमें एक root ने परिवर्तन से एक घंटा पहले जारी किया था।](https://raw.githubusercontent.com/het-p301204/afterlife/main/docs/figures/findings-vulnerable.svg)

`application recorded watermark: no` वह फ़ील्ड है जो किसी घटना प्रतिक्रियाकर्ता (incident responder) को लक्षण के बजाय *दोष* की ओर इंगित करता है।

### नियम जिस वॉटरमार्क का उपयोग करते हैं वह वह नहीं है जिसकी अनुप्रयोग रिपोर्ट करता है

क्रेडेंशियल-परिवर्तन इवेंट में एक `revocation_watermark` फ़ील्ड होता है — वह `credentials_valid_after` मान जो अनुप्रयोग ने लिखा था। **कमज़ोर कार्यान्वयन में यह `null` होता है, क्योंकि अनुप्रयोग ने कभी इसे लिखा ही नहीं।** यही बग है।

इसलिए उस फ़ील्ड पर आधारित कोई नियम उसी मामले के प्रति अंधा रह जाएगा जिसे पकड़ने के लिए वह अस्तित्व में है। नियम इवेंट के स्वयं के `timestamp` पर आधारित होते हैं, जो सत्य है चाहे अनुप्रयोग ने अपना काम किया हो या नहीं, और अनुपस्थित फ़ील्ड को साक्ष्य के रूप में रिपोर्ट करते हैं।

### गुण```text
  deduplication        6 accepted requests on the stale lineage -> 1 alert
  event order          shuffled stream -> same alert  (1 alert)
  duplicate telemetry  log replayed twice -> 1 alert  (24 duplicate events discarded)
  false positives      the fixed implementation's log -> 0 alerts
  bounded state        caps at 256 activity/user, 2000 users, 20000 credentials

क्रम-स्वतंत्रता "अधिकतर काम करती है" नहीं है: test_6b_every_permutation_of_the_critical_events_detects उन चार घटनाओं के सभी 24 क्रमों को चलाता है जो मायने रखती हैं और प्रत्येक से ठीक एक अलर्ट की मांग करता है। डुप्लिकेट-हटाना (user_id, credential_change_event_id, lineage_id) पर कुंजीबद्ध है, जिसके आगे event_id दमन है ताकि कोई रीप्ले की गई फ़ाइल गिनती को बढ़ा न सके।

पूर्ण नियम कार्ड, आवश्यक टेलीमेट्री और प्रतिक्रिया रनबुक: docs/detection.md।


कंसोल

उस एक प्रश्न के लिए एक पठन-सतह जिसके बारे में यह लैब है। अलर्ट गिनतियों का डैशबोर्ड नहीं — एक मृत्यु-दर रजिस्टर। प्रत्येक क्रेडेंशियल के लिए एक बार, जारी होने से मृत्यु तक, उस वंशावली में समूहित जिससे वह उतरा है, और पासवर्ड परिवर्तन को एक रेखा के रूप में खींचा गया है जिस पर उससे पहले की सब कुछ समाप्त होनी चाहिए थी।

vulnerable मोड में पूरा AFTERLIFE कंसोल: मास्टहेड, serif में निर्णय, पाँच-सेल का काउंटर स्ट्रिप, दो-फ़ील्ड की तुलना, गिलोटिन को पार करती पाँच बारों वाला लाइफ़लाइन चार्ट, और दोनों अलर्ट कार्ड।

एक जानबूझकर उलटाव: गर्माहट का अर्थ है जीवित, और रेखा के बाद गर्माहट ग़लत है। अधिकांश सुरक्षा UI में लाल का अर्थ है कोई त्रुटि हुई। यहाँ कुछ भी त्रुटि नहीं करता — vulnerable रन में हर अनुरोध 200 लौटाता है। इसलिए रंग मृत्यु-दर का अनुसरण करता है: ठंडा वह क्रेडेंशियल है जो मर गया जब उसे मरने को कहा गया, गर्म वह है जो अब भी साँस ले रहा है, और गिलोटिन के पार, अब भी गर्म होना ही पूरा निष्कर्ष है। (BLACKOUT ने वही परिपाटी अपनाई, जहाँ 200 लाल वाला था।)

पतली ऊर्ध्वाधर बूँदें वंशावली हैं: उसी क्षण अपने जनक से ढाला गया संतान क्रेडेंशियल। rt-001 → rt-002 → rt-004 पूरी विंडो में नीचे और दाईं ओर क़दम बढ़ाता है, और vulnerable मोड में यह कट के बाद भी क़दम बढ़ाता रहता है — जो किसी वंशावली के अपने ही विलोपन-घटना के दूसरी ओर नए क्रेडेंशियल ढालने का चित्र है।```bash python scripts/lab.py console

root@kitploit:~
**[`docs/console-preview.html`](https://github.com/het-p301204/afterlife/blob/main/docs/console-preview.html)** लिखता है — एक
स्व-निहित 74 KiB फ़ाइल जिसमें दोनों रन अंतर्निहित हैं। कोई सर्वर नहीं, कोई नेटवर्क नहीं, कोई
फ़ॉन्ट लाने की आवश्यकता नहीं; इसे फ़ाइलसिस्टम से खोलें। या लाइव संस्करण चलाएँ:```bash
python -m console

स्क्रबर को 10:00:00 से पीछे और आगे की ओर खींचें: निष्कर्ष तब प्रकट होते हैं जब लॉग उन्हें अर्जित करता है, AFTERLIFE-003 परिवर्तन पर और AFTERLIFE-001 तीन सेकंड बाद। डिज़ाइन तर्क, जिसमें क्या काटा गया, यह भी शामिल है, docs/console-design.md में है।

कंसोल निर्माण से ही केवल-पठन है। यह पूर्ण हो चुके परिदृश्यों को फिर से चलाता है और उन्हें चित्रित करता है; यह कार्यान्वयन को स्विच नहीं कर सकता, घड़ी को हिला नहीं सकता या कुछ भी रद्द नहीं कर सकता। test_the_console_has_no_control_surface यह दावा करता है कि एकमात्र गैर-GET रूट /api/rebuild है। एक पठन सतह जो जो पढ़ रही है उसे बदल सकती है, वह भरोसेमंद नहीं हो सकती।


आर्किटेक्चर

छह पैकेज, एक सुरक्षा वर्ग, कोई अवसंरचना नहीं।```text app/ the lab application config.py mode selection; defaults to fixed, deliberately store.py SQLite credential state + the security-change audit trail tokens.py minting and decoding; a JWT is a signed pointer to a row auth.py login / refresh / change-password + THE BUG + the audit main.py six endpoints

common/ clock.py a rewindable UTC clock, so an hour of history costs nothing events.py the event vocabulary, shared by app and detector telemetry.py JSONL emission with credential-name redaction

detector/ strictly downstream: reads a log file, decides nothing base.py the alert shape, replay suppression, bounded state lineage.py rebuilds a credential's ancestry from issuance events ledger.py which lineages are alive, per user rules.py AFTERLIFE-001 persistence reuse.py AFTERLIFE-002 theft containment.py AFTERLIFE-003 the defect itself naive.py NAIVE-001, kept in order to be demonstrated failing audit.py the state scan the rules structurally cannot do engine.py the pack: one shared index, one stream, ranked alerts tail.py byte-offset JSONL tailer

console/ the mortality register payload.py one run, shaped for drawing static/ ~1400 lines of vanilla HTML/CSS/JS, no build step

scripts/ lab.py the demonstration scenarios.py nine scenarios x both implementations report.py the incident report preview.py bake the offline console figures.py render the console to SVG for this README

tests/ 290 tests

root@kitploit:~
The application runs **in-process** under the demo, the console और tests। No
ports, no dev server, no Docker। The clock pinned है, so every run
deterministic है और committed telemetry, console, figures और reports सभी
runs के बीच byte-identical हैं — जिसे CI `git diff --exit-code` के साथ check करता है।

### Server-side enforcement

A credential एक **row** है, string नहीं। JWT जो client hold करता है वह `cid` carry करता है —
एक lookup key — और row decide करती है कि credential alive है या नहीं।

Expiry को row के `expires_at` के against check किया जाता है, न कि `exp` claim के against जो
holder ने present किया। Both temporal claims PyJWT को `verify=False` के साथ handed हैं,
जिसका यहाँ मतलब है *"the server checks this itself"*, और [`app/auth.py`](https://github.com/het-p301204/afterlife/blob/main/app/auth.py)
हर request पर ऐसा करता है। Client को कभी vote नहीं मिलता कि उसका own
credential अभी भी valid है या नहीं।

---

## Attack timeline```mermaid
flowchart TD
    A["Account compromised<br/><i>phishing · XSS · stolen backup</i>"] --> B["Attacker holds refresh credential rt-001<br/>lineage lin-001, root sess-001 @ 09:00"]
    B --> C["Legitimate password change @ 10:00<br/>HTTP 200 · password hash updated"]
    C --> D{"device_id supplied?"}
    D -->|"yes"| E["revoke_credentials(user, device_id)<br/>rt-001 revoked"]
    D -->|"no — the exploit"| F["nothing revoked<br/>no watermark written"]
    E --> G["Attacker refresh → 401<br/><b>contained</b>"]
    F --> AF3["<b>AFTERLIFE-003 · HIGH</b> @ 10:00:00<br/>containment did not run<br/><i>no attacker action required</i>"]
    F --> H["POST /refresh rt-002 → 200<br/>mints at-004 @ 10:00:03"]
    H --> I["GET /me at-004 → 200<br/>well-formed · correctly signed · <b>no anomaly</b>"]
    I --> J["at-004.issued_at > watermark<br/>NAIVE-001: no alert"]
    I --> K["lineage lin-001 root @ 09:00 < watermark<br/><b>AFTERLIFE-001 · HIGH</b>"]
    F --> L["attacker holds and never spends<br/>AFTERLIFE-001 silent — correctly<br/><b>state audit: dormant survivor</b>"]

    style F fill:#7f1d1d,color:#fff
    style H fill:#7f1d1d,color:#fff
    style I fill:#7f1d1d,color:#fff
    style J fill:#78350f,color:#fff
    style K fill:#14532d,color:#fff
    style AF3 fill:#14532d,color:#fff
    style L fill:#1e3a5f,color:#fff
    style E fill:#14532d,color:#fff
    style G fill:#14532d,color:#fff

बीच की refresh-chain शाखा ही पूरा मुद्दा है। हमलावर पुरानी credential का उपयोग नहीं करता। वे एक पुरानी lineage का उपयोग करते हैं, और lineage मांग पर कुछ नया mint करती है।


Token lineage

एक session किसी lineage का root होता है। उसके नीचे mint की गई हर चीज़ उस root को हमेशा के लिए inherit करती है।```text sess-001 session lineage lin-001 root sess-001 issued 09:00:00 │ ├── rt-001 refresh parent sess-001 root_issued_at 09:00:00 │ └── at-001 access parent rt-001 root_issued_at 09:00:00 │ ├── rt-002 refresh parent rt-001 root_issued_at 09:00:00 ← 09:30 rotation │ └── at-002 access parent rt-002 root_issued_at 09:00:00 │ └── rt-004 refresh parent rt-002 root_issued_at 09:00:00 ← 10:00:03 rotation └── at-004 access parent rt-004 root_issued_at 09:00:00 issued_at 10:00:03

root@kitploit:~
`at-004` तीन सेकंड पुराना है। इसका वंश एक घंटे पुराना है। दोनों तथ्य सत्य हैं,
और इनमें से केवल एक ही अनुरोध लॉग में दिखाई देता है।

दो अपरिवर्तनीय नियम इसे एक साथ बनाए रखते हैं, और दोनों का परीक्षण किया गया है:

1. **केवल प्रमाणीकरण एक वंशावली बनाता है।** `Store.open_session` एकमात्र स्थान है जहाँ
   `lineage_id` उत्पन्न होता है, और यह सत्र को उसका स्वयं का
   `root_credential_id` बना देता है।
2. **मिंटिंग रूट को नीचे कॉपी करती है।** `TokenService.mint` वंशावली फ़ील्ड्स को
   पुनर्गणना करने के बजाय पैरेंट से पढ़ता है, इसलिए एक क्रेडेंशियल उस लॉगिन से अधिक ताज़ा वंशावली प्राप्त नहीं कर सकता जिससे वह उतरा है।

धारक-प्रस्तुत टोकन जानबूझकर अपना वंशावली रूट **नहीं** वहन करता। यदि वह करता, तो धारक इसके बारे में झूठ बोल सकता था।

---

## स्थिति ऑडिट

AFTERLIFE-001 तब सक्रिय होता है जब एक पुरानी वंशावली *उपयोग* की जाती है। यह एक डिटेक्शन नियम के लिए सही ट्रिगर है, और यह एक छेद छोड़ देता है: **जिस पुरानी वंशावली को किसी ने छुआ नहीं है, वह इसके लिए अदृश्य है।** एक हमलावर जो एक क्रेडेंशियल चुराता है, रीसेट को विफल होते देखता है, और फिर प्रतीक्षा करता है, सहसंबंधित करने के लिए कोई गतिविधि उत्पन्न नहीं करता।

इसलिए लैब वह प्रश्न भी पूछता है जो एक नियम नहीं पूछ सकता: *क्या हुआ* नहीं, बल्कि *क्या अभी भी जीवित है*।```text
  scenario: dormant-survivor   (the attacker holds the credential and never spends it)

  AFTERLIFE-001   silent   — correct; nothing was accepted
  AFTERLIFE-003   HIGH     — the change revoked nothing
  state audit     1 stale lineage, 1 dormant

| -s | --server | Server URL (default: http://localhost:8080) | | -t | --token | API token for authentication | | -o | --output | Output file path | | -f | --format | Output format: json, yaml, table | | -v | --verbose | Enable verbose logging | | -q | --quiet | Suppress non-essential output | | | | Show help message | | | | Show version information |

उदाहरण

root@kitploit:~
# Scan a single target
scanner scan --target example.com

# Scan multiple targets from a file
scanner scan --input targets.txt --output results.json

# Use a custom configuration file
scanner scan --config /path/to/config.yaml

# Run with verbose output
scanner scan --target example.com --verbose

कॉन्फ़िगरेशन

The scanner can be configured using a YAML configuration file. By default, it looks for config.yaml in the current directory.

root@kitploit:~
# Scanner configuration
server:
  url: "http://localhost:8080"
  timeout: 30

scan:
  threads: 10
  timeout: 60
  retries: 3

output:
  format: "json"
  directory: "./results"

API उपयोग

The scanner exposes a REST API that can be used to integrate with other tools.

root@kitploit:~
# Start a scan
curl -X POST http://localhost:8080/api/v1/scan \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"target": "example.com"}'

# Get scan status
curl http://localhost:8080/api/v1/scan/SCAN_ID \
  -H "Authorization: Bearer YOUR_TOKEN"

योगदान

Contributions are welcome! Please follow these steps:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

लाइसेंस

This project is licensed under the MIT License - see the LICENSE file for details.```bash python scripts/lab.py audit

root@kitploit:~
| `--no-color` | Disable colored output |
| `--debug` | Enable debug mode |
| `--verbose` | Enable verbose mode |
| `--silent` | Enable silent mode |
| `--version` | Show version and exit |
| `--help` | Show help and exit |```text
VULNERABLE
  server state   1 lineage(s) outlived the change   (0 dormant)
  telemetry      1 lineage(s)   -- the same question, asked of the log instead of the database
    lin-001 root 2026-09-11T09:00:00.000Z  7 credentials  in use

FIXED
  server state   clean
  telemetry      clean

दो स्रोत, जानबूझकर: telemetry ऑडिट देखता है कि लॉग क्या साबित कर सकता है, server-state ऑडिट देखता है कि डेटाबेस क्या मानता है। जहाँ वे असहमत होते हैं, लॉग क्रेडेंशियल स्थिति का एक विश्वसनीय रिकॉर्ड नहीं है, और उस पर बनाई गई हर डिटेक्शन दिखने से कमज़ोर होती है — इसलिए कमांड दोनों को प्रिंट करता है और यदि वे भिन्न हों तो ऐसा कहता है।

server-state ऑडिट को security_changes तालिका की आवश्यकता होती है, जिसे दोनों मोड लिखते हैं, क्योंकि यह रिकॉर्ड करना कि कोई परिवर्तन हुआ, उस पर कार्य करने से एक अलग दायित्व है — और कमज़ोर कार्यान्वयन दोनों में से ठीक एक को पूरा करता है।


परिदृश्य मैट्रिक्स

दोनों कार्यान्वयनों के विरुद्ध नौ परिदृश्य। python scripts/scenarios.py अपने स्वयं के दो अपरिवर्तनीयों का दावा करता है और यदि उनमें से कोई भी टूटे तो गैर-शून्य के साथ बाहर निकल जाता है।

चार पंक्तियाँ एक तर्क रखती हैं:

  • legitimate-only में कोई हमलावर बिल्कुल नहीं है। फिक्स कुछ भी उत्पन्न नहीं करता; कमज़ोर कार्यान्वयन एक LOW उत्पन्न करता है। यह एक गलत सकारात्मक नहीं है — यह enumeration_only है: निरसन इस बार काम कर गया, गणना द्वारा, बिना किसी वॉटरमार्क के जो सर्वर द्वारा भुलाए गए क्रेडेंशियल को कवर कर सके। पैक बिना किसी हमलावर की उपस्थिति में दोनों कार्यान्वयनों को अलग करता है।
  • dormant-survivor एक पंक्ति में ब्लाइंड स्पॉट और उसका उत्तर दोनों है: AFTERLIFE-001 मौन, AFTERLIFE-003 HIGH, ऑडिट dormant: 1।
  • multi-device वह जगह है जहाँ कमज़ोर कार्यान्वयन की कार्यरत शाखा अभी भी विफल होती है — निरसन लैपटॉप तक सीमित, अन्य दो डिवाइस अछूते।
  • expired-lineage गलत-सकारात्मक नियंत्रण है: 40 दिन पुराना सत्र जिसके क्रेडेंशियल स्वयं समाप्त हो गए, एक जीवित पर्सिस्टेंस पथ नहीं है, और इसे वैसा रिपोर्ट नहीं किया जाता।

और मैट्रिक्स का केंद्रीय दावा, जिसे स्क्रिप्ट और test_the_fix_never_produces_a_control_failure_finding द्वारा जाँचा गया: फिक्स किसी भी परिदृश्य में कोई control-failure finding उत्पन्न नहीं करता। AFTERLIFE-002 को गुजरने दिया जाता है — यह एक चोरी की रिपोर्ट करता है, control failure की नहीं, और एक सही कार्यान्वयन में भी रिपोर्ट करने के लिए चोरियाँ होती हैं।


गलत सकारात्मक

एक नियम जो हर पासवर्ड रीसेट पर पेज भेजता है, एक सप्ताह के भीतर म्यूट कर दिया जाता है, और एक म्यूट किया गया नियम किसी भी नियम से बदतर है — यह एक ऐसा नियम है जिसे हर कोई मानता है कि चल रहा है।

प्रत्येक पंक्ति का एक टेस्ट है tests/test_detector_afterlife001.py और tests/test_detector_rulepack.py में।

2-सेकंड सहनशीलता पर। इस लैब में प्रत्येक टाइमस्टैम्प एक प्रक्रिया और एक घड़ी से आता है, इसलिए ईमानदार सहनशीलता शून्य है। 2s वह है जो NTP के अंतर्गत एक यथार्थवादी दो-होस्ट परिनियोजन को चाहिए, और यह इस हमले द्वारा उत्पन्न अंतरालों से तीन कोटि छोटा है — एक refresh क्रेडेंशियल का पूरा उद्देश्य ही दीर्घायु होना है। सहनशीलता एक ऐसी विंडो है जिसमें एक वास्तव में stale lineage को अनदेखा किया जाता है, इसलिए इसे जानबूझकर छोटा रखा गया है: test_12b_a_gap_beyond_the_tolerance_does_alert यह पिन करता है कि 2.1s अभी भी फायर करता है।


फिक्स```python

THE FIX. Two mechanisms, deliberately redundant.

self.store.set_watermark(user.user_id, moment) # the guarantee revoked = self.store.revoke_credentials(user.user_id, ...) # defence in depth

root@kitploit:~
न तो `device_id` पर निर्भर करता है। इसे संदर्भ के रूप में दर्ज किया जाता है और ब्लास्ट रेडियस में इसका कोई योगदान नहीं है।

**वॉटरमार्क ही आर्किटेक्चरल गारंटी है।** प्रति उपयोगकर्ता एक टाइमस्टैम्प, `credentials_valid_after`, प्रत्येक अनुरोध पर क्रेडेंशियल के स्वयं के जारी होने *और* उसके lineage रूट दोनों के विरुद्ध तुलना किया जाता है:```python
watermark = user.credentials_valid_after
if credential.issued_at      < watermark:  reject  # the obvious case
if credential.root_issued_at < watermark:  reject  # the refresh chain

दूसरी तुलना वह है जिसे सही करने में कुछ खर्च होता है, और जिसे एक भोली-भाली कार्यान्वयन छोड़ देती है — प्रवर्तन में भी ठीक वैसे ही जैसे पहचान में।

स्पष्ट निरसन रक्षा में गहराई है, और प्रमाण भी। revoked_at और revocation_reason वही हैं जो एक घटना प्रतिक्रियाकर्ता पढ़ता है, और auth.token.revoked घटनाएँ वही हैं जो साबित करती हैं कि नियंत्रण हुआ।

test_the_watermark_alone_rejects_a_stale_credential वॉटरमार्क को बिना कुछ भी निरसित किए सेट करता है और अपेक्षा करता है कि पुराना क्रेडेंशियल अस्वीकार कर दिया जाए — जो यह स्थापित करता है कि दोनों में से कौन भार वहन करने वाला है, और कौन सा गुण उन क्रेडेंशियल्स के लिए अभी भी मान्य रहता है जिन्हें सर्वर यह भूल गया है कि उसने जारी किया था।

वही हमला, फिक्स के विरुद्ध

फिक्स्ड मोड में कंसोल: निर्णय पढ़ता है "परिवर्तन से पहले जारी किया गया कुछ भी इसके बाद काम नहीं करता", काउंटर सभी हरे हैं, lin-001 में प्रत्येक बार गिलोटिन पर समाप्त होता है, और निष्कर्ष पैनल पाँच पासिंग जाँचों के साथ कोई निष्कर्ष नहीं बताता।

एक भी अनुरोध नहीं बदला। एक फ़ंक्शन बदला।

केवल पासवर्ड नहीं

ट्रिगर "पासवर्ड बदल गया" नहीं है। यह "कुछ ऐसा बदल गया जो पहले जारी किए गए क्रेडेंशियल्स को अविश्वसनीय बनाता है" है। सभी चार मार्ग _revoke_for_security_change साझा करते हैं, इसलिए फिक्स और बग समान रूप से लागू होते हैं:

प्रत्येक प्रकार के लिए परीक्षण किया गया, दोनों मोड में, tests/test_privilege_changes.py में — जिसमें लॉगिन की गैर-आवश्यकता भी शामिल है, जिसे प्रलेखित किया गया है ताकि अगला पाठक इसे "ठीक" न कर दे।


वास्तुशिल्पीय समझौते

किसी क्रेडेंशियल को काम करना बंद कराने के चार तरीके। AFTERLIFE A + C लागू करता है, और क्रम मायने रखता है।

D पर: JWT स्वाभाविक रूप से असुरक्षित नहीं हैं। तनाव संकरा है और इसे सटीकता से कहना उचित है — स्टेटलेस सत्यापन और तत्काल सर्वर-साइड निरसन परस्पर अनन्य हैं। आप "यह क्रेडेंशियल अब वैध नहीं है" यह तय नहीं कर सकते, बिना किसी ऐसी चीज़ से परामर्श किए जो यह जानती हो, और उससे परामर्श करना ही सिस्टम को स्टेटफुल बनाता है। गलती यह है कि JWT को उनकी स्टेटलेसनेस के लिए अपनाया जाए और फिर भी निरसन की आवश्यकता पड़े, जो हर उत्पाद तब करता है जब पहली बार लैपटॉप चोरी होता है। अंत में आपके पास जो बचता है वह सर्वर-साइड स्थिति की ओर एक हस्ताक्षरित संकेतक है — जिसे app/tokens.py जानबूझकर लागू करता है, क्योंकि वहाँ जानबूझकर पहुँचना घटना के बीच में पहुँचने से सस्ता है।

2.1 Low पर बहस करना गलत बात क्यों है

CVE-2026-22706 का स्कोर 2.1, Low है (CVSS v4.0 AV:N/AC:H/AT:N/PR:H/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N), और यह स्कोर उचित है: PR:H क्योंकि हमलावर के पास पहले से ही एक वैध रीफ्रेश क्रेडेंशियल होना चाहिए, AC:H क्योंकि इसे प्राप्त करने के लिए पूर्व समझौता आवश्यक है, और VC:N/VI:L क्योंकि यह दोष हमलावर को कोई ऐसी पहुँच नहीं देता जो उसके पास पहले से न हो। CVSS भेद्यता के सीमांत प्रभाव को मापता है, और सीमांत प्रभाव पहुँच की अवधि है, उसका दायरा नहीं। इन इनपुट्स को देखते हुए, 2.1 सूत्र से सही ढंग से निकलता है।

CVSS जिसे मॉडल नहीं करता वह यह है कि विफल नियंत्रण नियंत्रण क्रिया है — इसलिए उपयोगी निष्कर्ष ट्राइएज रूटिंग के बारे में है, संख्या के बारे में नहीं। एक नियंत्रण पथ में Low उस ध्यान का पात्र है जो एक फ़ीचर पथ में Low नहीं है, क्योंकि लागत घटना के दौरान चुकाई जाती है। पूरी चर्चा: docs/tradeoffs.md।


परीक्षण```text

290 passed

root@kitploit:~
| file | यह क्या pin करता है |
|---|---|
| [`test_fixed_mode.py`](https://github.com/het-p301204/afterlife/blob/main/tests/test_fixed_mode.py) | **वह regression suite जो कभी red नहीं होनी चाहिए** — revocation event से पहले जारी कोई भी credential उसके बाद स्वीकार नहीं किया जाता, जिसमें एक stale lineage से उत्पन्न credential भी शामिल है |
| [`test_vulnerable_mode.py`](https://github.com/het-p301204/afterlife/blob/main/tests/test_vulnerable_mode.py) | vulnerability **मौजूद है**, deterministic है, 30 दिन तक रहती है, और conditional के कारण होती है — `device_id` control यह साबित करता है कि revocation path optional है, dead नहीं |
| [`test_lineage.py`](https://github.com/het-p301204/afterlife/blob/main/tests/test_lineage.py) | एक refresh chain एक lineage और एक root रखती है; केवल login एक lineage बनाता है |
| [`test_detector_afterlife001.py`](https://github.com/het-p301204/afterlife/blob/main/tests/test_detector_afterlife001.py) | 7 detection cases, 9 false-positive cases, 2 blind spots, सभी 24 event orderings, bounded state, malformed input |
| [`test_detector_rulepack.py`](https://github.com/het-p301204/afterlife/blob/main/tests/test_detector_rulepack.py) | AFTERLIFE-002 और AFTERLIFE-003 — हर verdict, grace window, rotation-is-not-death, expiry exclusion, और engine की ordering |
| [`test_naive_detector.py`](https://github.com/het-p301204/afterlife/blob/main/tests/test_naive_detector.py) | NAIVE-001 **उसी विशिष्ट तरीके से** fail होता रहता है जिसका दावा यह README करता है — जिसमें यह भी शामिल है कि उसके पास आवश्यक data था |
| [`test_audit_and_console.py`](https://github.com/het-p301204/afterlife/blob/main/tests/test_audit_and_console.py) | वह dormant survivor जिसे कोई rule नहीं देख सकता; दोनों audit sources का सहमत होना; console का अपना कोई verdict न निकालना; offline preview का कुछ भी fetch न करना; इस README का हर figure in bounds, stylesheet-free और byte-stable होना |
| [`test_scenarios.py`](https://github.com/het-p301204/afterlife/blob/main/tests/test_scenarios.py) | वे matrix rows जो एक argument रखती हैं |
| [`test_privilege_changes.py`](https://github.com/het-p301204/afterlife/blob/main/tests/test_privilege_changes.py) | MFA, role change और account recovery, दोनों modes |
| [`test_telemetry.py`](https://github.com/het-p301204/afterlife/blob/main/tests/test_telemetry.py) | redaction contract, और जारी किए गए literal bearer strings के लिए log का grep |
| [`test_app.py`](https://github.com/het-p301204/afterlife/blob/main/tests/test_app.py) | forged signatures, गलत credential types, server state से expiry |
| [`test_infrastructure.py`](https://github.com/het-p301204/afterlife/blob/main/tests/test_infrastructure.py) | config, clock, tailer partial lines, दोनों CLIs, हर demo command |

Suite का सबसे मजबूत test है
`test_no_bearer_string_or_password_ever_reaches_the_log`: यह पूरा attack चलाता है
और फिर log file में application द्वारा दिए गए actual credentials खोजता है, साथ ही
दोनों passwords, और signing key भी। Field names के लिए नहीं — values के लिए।

---

## Blind spots

स्पष्ट रूप से बताए गए हैं, क्योंकि इन्हें छिपाना lab को बेईमान बना देगा। प्रत्येक का एक test है।

> **यदि application credential-change events emit नहीं करता, तो pack post-reset
> activity को विश्वसनीय रूप से correlate नहीं कर सकता।** कोई anchor नहीं है, इसलिए
> activity के "after" होने के लिए कुछ नहीं है — और यह पहले AFTERLIFE-003 को चुप करा
> देता है, जो वह rule है जो आपको बताता कि control टूटा हुआ था।
>
> **यदि application issuance/lineage metadata retain नहीं करता, तो detector यह
> निर्धारित नहीं कर सकता कि कोई fresh access token किसी पुराने credential से
> उत्पन्न हुआ था या नहीं।** तीन सेकंड पहले mint किया गया credential, एक महीने पुरानी
> chain द्वारा तीन सेकंड पहले mint किए गए credential से अलग नहीं पहचाना जा सकता।

ये **optional logging preferences नहीं हैं। ये security detection requirements हैं।**
Log volume कम करने के लिए `auth.token.issued` हटाना logging को सस्ता नहीं बनाता;
यह एक detection बंद कर देता है।

Unresolvable activity को **counted** किया जाता है, चुपचाप drop नहीं किया जाता —
`stats()["activity_with_unresolved_lineage"]` तब non-zero होता है जब भी detector से
ऐसे credentials के बारे में पूछा गया जिन्हें telemetry ने कभी describe नहीं किया।
एक rule जो इसलिए चुप है क्योंकि कुछ गलत नहीं है और एक rule जो इसलिए चुप है क्योंकि
वह blind है, बाहर से एक जैसे दिखते हैं, और वह संख्या यही अंतर है।

यह भी सत्य है, और यह भी documented है:

* **Detector किसी request को refuse नहीं कर सकता।** यह एक log tail करता है। यह
  आपको बताता है कि containment fail हुआ; यह contain नहीं करता।
* **Audit एक scan है, detection नहीं।** यह dormant-survivor gap को बंद करता है,
  लेकिन यह तब चलता है जब कोई इसे चलाता है। यह page नहीं कर सकता।
* **Fix reset के *बाद* चुराए गए credentials में मदद नहीं करता।** Containment एक
  point in time है, कोई property नहीं।
* **AFTERLIFE-003 observed containment verify करता है, architectural completeness
  नहीं।** एक device-scoped revocation जो संयोग से सब कुछ cover कर लेती है, उसे LOW
  finding मिलती है, HIGH नहीं। Bug अभी भी वहीं है; उस account पर उसने काटा नहीं।
  एक test पूछ सकता है "दो devices के बारे में क्या?"; एक log केवल वही report कर सकता
  है जो वास्तव में हुआ।
* **अन्य persistence password change के बाद पूरी तरह जीवित रहती है** — OAuth grants,
  API keys, mail-forwarding rules, recovery contacts।

पूरी चर्चा: **[docs/limitations.md](https://github.com/het-p301204/afterlife/blob/main/docs/limitations.md)**।

---

## Running the lab```bash
python -m venv .venv
source .venv/bin/activate          # Windows: .venv\Scripts\activate

pip install -r requirements.txt -r requirements-dev.txt

pytest -q

फिर, क्रम में:```bash python scripts/lab.py vulnerable

root@kitploit:~
| `-s` | `--server` | Server URL (default: `http://localhost:8080`) |
| `-t` | `--token` | API token for authentication |
| `-o` | `--output` | Output file path |
| `-f` | `--format` | Output format: `json`, `yaml`, `table` |
| `-v` | `--verbose` | Enable verbose output |
| `-q` | `--quiet` | Suppress non-essential output |
| `-h` | `--help` | Show help message |
| `-V` | `--version` | Show version information |

### उदाहरण

```bash
# List all projects
kitploit-cli projects list

# Get details of a specific project
kitploit-cli projects get --id 12345

# Create a new scan
kitploit-cli scans create --target https://example.com --profile full

# Export results to JSON
kitploit-cli scans results --id 67890 --format json --output results.json

कॉन्फ़िगरेशन

The CLI reads configuration from ~/.kitploit/config.yaml:

root@kitploit:~
server: http://localhost:8080
token: your-api-token-here
output_format: table
verbose: false

Environment variables can override configuration file settings:

त्रुटि प्रबंधन

The CLI returns the following exit codes:

For detailed error messages, use the --verbose flag.```bash python scripts/lab.py detect

root@kitploit:~
| `--no-color` | Disable colored output |
| `--debug` | Enable debug mode |
| `--verbose` | Enable verbose mode |
| `--silent` | Enable silent mode |
| `--version` | Show version |
| `--help` | Show help |```bash
python scripts/lab.py fixed

| --no-color | रंगीन आउटपुट अक्षम करें | | --debug | डिबग लॉगिंग सक्षम करें | | --verbose | विस्तृत लॉगिंग सक्षम करें | | --silent | मौन मोड सक्षम करें | | --version | संस्करण जानकारी दिखाएं | | --help | सहायता संदेश दिखाएं |```bash python scripts/lab.py audit

root@kitploit:~
या पूरी कहानी एक साथ, अंत में सीख के साथ:```bash
python scripts/lab.py all

और चित्र:```bash python scripts/lab.py console

root@kitploit:~
<details>
<summary><b>बाकी सब कुछ</b> — डिटेक्टर CLI, मैट्रिक्स, रिपोर्ट, आंकड़े, सर्वर</summary>

<br>

कमिट किए गए साक्ष्य को पैक के माध्यम से रीप्ले करें। किसी एप्लिकेशन रन की आवश्यकता नहीं — डिटेक्टर पूरी तरह से डाउनस्ट्रीम है, और यह प्रमाण है:```bash
python -m detector --once --timeline --events evidence/vulnerable-persistence.jsonl

एक बार में एक नियम, या भोला वाला, या स्थिति स्कैन:```bash python -m detector --once --rule AFTERLIFE-003 --events evidence/vulnerable-persistence.jsonl

root@kitploit:~
| `-s` | `--server` | Server URL (default: `http://localhost:8080`) |
| `-t` | `--token` | API token for authentication |
| `-o` | `--output` | Output file path |
| `-f` | `--format` | Output format: `json`, `yaml`, `table` |
| `-v` | `--verbose` | Enable verbose logging |
| `-q` | `--quiet` | Suppress non-error output |
| `-h` | `--help` | Show help message |
| `-V` | `--version` | Show version information |

### उदाहरण

```bash
# Scan a single target
scanner scan --target example.com

# Scan multiple targets from a file
scanner scan --input targets.txt --output results.json

# Use a custom configuration
scanner scan --config /path/to/config.yaml --verbose

कॉन्फ़िगरेशन

The scanner reads its configuration from ~/.scanner/config.yaml by default. You can override this with the --config flag.

root@kitploit:~
server:
  url: http://localhost:8080
  token: your-api-token
  timeout: 30s

scan:
  concurrency: 10
  rate_limit: 100
  retries: 3

output:
  format: json
  directory: ./results

API संदर्भ

The REST API is available at /api/v1/. All endpoints require authentication via the Authorization: Bearer <token> header.

त्रुटि प्रबंधन

The scanner returns the following exit codes:

योगदान

Contributions are welcome! Please read the CONTRIBUTING.md file for guidelines.

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

लाइसेंस

This project is licensed under the MIT License - see the LICENSE file for details.

आभार

  • ProjectDiscovery for their amazing tools
  • All contributors who have helped shape this project```bash python -m detector --once --naive --events evidence/vulnerable-persistence.jsonl
root@kitploit:~
| `--no-color` | Disable colored output |
| `--debug` | Enable debug mode |
| `--verbose` | Enable verbose mode |
| `--silent` | Enable silent mode |
| `--version` | Show version |
| `--help` | Show help |```bash
python -m detector --audit --events evidence/vulnerable-persistence.jsonl

CLI कुछ पाता है तो 1 के साथ बाहर निकलता है, इसलिए यह अपने आउटपुट को पार्स किए बिना CI चेक के रूप में काम करता है।

दोनों कार्यान्वयनों के विरुद्ध नौ परिदृश्य, स्व-जाँच अपरिवर्तनीयों के साथ:```bash python scripts/scenarios.py

root@kitploit:~
जिस घटना रिपोर्ट को एक प्रतिक्रियाकर्ता को सौंपा जाएगा, और इस README में दिए गए आंकड़े:```bash
python scripts/report.py

| -s | --server | Server URL (default: http://localhost:8080) | | -t | --token | Authentication token | | -o | --output | Output file path | | -f | --format | Output format: json, yaml, table | | -v | --verbose | Enable verbose logging | | -q | --quiet | Suppress non-error output | | | | Show help message |

उदाहरण

root@kitploit:~
# Scan a single target
scanner -t example.com

# Scan multiple targets from a file
scanner -f targets.txt -o results.json

# Use a custom server with authentication
scanner -s https://api.example.com -t YOUR_TOKEN -v

कॉन्फ़िगरेशन

The tool reads configuration from ~/.scanner/config.yaml:

root@kitploit:~
server: http://localhost:8080
token: your-token-here
timeout: 30
threads: 10

आउटपुट फ़ॉर्मेट

Results can be exported in multiple formats:

  • JSON: Machine-readable output for automation
  • YAML: Human-readable structured output
  • Table: Formatted console output

त्रुटि प्रबंधन

Common error codes and their meanings:

योगदान

We welcome contributions! Please follow these steps:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

लाइसेंस

This project is licensed under the MIT License - see the LICENSE file for details.```bash python scripts/figures.py

root@kitploit:~
लाइव कंसोल, और लैब API हाथ से — दोनों केवल लूपबैक:```bash
python -m console

| -s | --server | Server URL (default: http://localhost:8080) | | -t | --token | API token for authentication | | -o | --output | Output file path | | -f | --format | Output format (json, yaml, table) | | -v | --verbose | Enable verbose output | | -q | --quiet | Suppress non-essential output | | | | Show help message |

उदाहरण

root@kitploit:~
# Scan a single target
scanner scan --target example.com

# Scan multiple targets from a file
scanner scan --input targets.txt --output results.json

# Use a custom configuration
scanner scan --config custom-config.yaml --verbose

कॉन्फ़िगरेशन

The scanner can be configured using a YAML configuration file:

root@kitploit:~
server:
  url: "http://localhost:8080"
  token: "your-api-token"
  timeout: 30

scan:
  threads: 10
  timeout: 60
  retries: 3

output:
  format: "json"
  directory: "./results"

API संदर्भ

POST /api/v1/scan

Start a new scan.

अनुरोध:

root@kitploit:~
{
  "target": "example.com",
  "options": {
    "depth": 3,
    "timeout": 60
  }
}

प्रतिक्रिया:

root@kitploit:~
{
  "id": "scan-12345",
  "status": "running",
  "created_at": "2024-01-15T10:30:00Z"
}

GET /api/v1/scan/{id}

Retrieve scan results by ID.

प्रतिक्रिया:

root@kitploit:~
{
  "id": "scan-12345",
  "status": "completed",
  "results": [
    {
      "type": "vulnerability",
      "severity": "high",
      "description": "SQL injection detected"
    }
  ]
}

योगदान

We welcome contributions! Please follow these steps:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

लाइसेंस

This project is licensed under the MIT License - see the LICENSE file for details.```bash python -m app --mode vulnerable --port 9101

root@kitploit:~
कमिट किए गए टेलीमेट्री को पुनः उत्पन्न करें (पिन किया गया क्लॉक, रन के बीच बाइट-समान):```bash
python scripts/lab.py evidence

कार्य रनर, दोनों तरह से समान लक्ष्य:```bash make demo

root@kitploit:~
| `--no-color` | आउटपुट में रंग अक्षम करें |
| `--debug` | डिबग मोड सक्षम करें |
| `--verbose` | वर्बोज़ आउटपुट सक्षम करें |
| `--quiet` | चुप्पी मोड सक्षम करें |
| `--version` | संस्करण जानकारी दिखाएं |
| `--help` | सहायता संदेश दिखाएं |```powershell
./make.ps1 demo

लैब खाता```text

alice / Password123! → changed during the demo to Correct-Horse-Battery-9!

root@kitploit:~
Fake, local, और इस repository में केवल यही credentials हैं।

---

## Security notes

**इस repository में जानबूझकर vulnerable code मौजूद है।**
`app/auth.py:_revoke_for_security_change` जब `AFTERLIFE_MODE=vulnerable` होता है तो जानबूझकर revoke करने में विफल रहता है।

Vulnerable path **default नहीं है**। `AFTERLIFE_MODE` का default `fixed` है, और `test_the_default_mode_is_the_safe_one` इसे pin करता है — ऐसा lab जिसका टूटा हुआ व्यवहार कुछ भी configure करना भूल जाने पर मिलता है, आखिरकार किसी असली चीज़ में copy हो जाएगा।

किसी भी mode में इसे network पर expose करना सुरक्षित नहीं है: bearer credentials response bodies में लौटाए जाते हैं, `GET /lab/credentials` किसी user की credential state के बारे में server का पूरा दृश्य dump कर देता है, `GET /lab/audit` design से unauthenticated है, `POST /security-change` स्वयं को `admin` grant कर लेगा, और कहीं भी TLS, rate limiting, CSRF protection या account lockout नहीं है। सब कुछ `127.0.0.1` पर bind होता है, और `python -m app` तथा `python -m console` दोनों किसी अन्य address को अस्वीकार करते हैं।

इस repository में कोई असली credential, key या secret मौजूद नहीं है। Signing key literal string `afterlife-lab-signing-key-not-a-secret-do-not-reuse` है; credential database का default `:memory:` है; telemetry उन values को बदल देती है जिनके key names credentials जैसे दिखते हैं और credentials को id तथा fingerprint से संदर्भित करती है। विवरण और reporting प्रक्रिया: **[SECURITY.md](https://github.com/het-p301204/afterlife/blob/main/SECURITY.md)**।

---

## Documentation

| | |
|---|---|
| [docs/detection.md](https://github.com/het-p301204/afterlife/blob/main/docs/detection.md) | rule pack: सभी तीन rule cards, state audit, आवश्यक telemetry, false positives, severity rationale, response runbooks |
| [docs/tradeoffs.md](https://github.com/het-p301204/afterlife/blob/main/docs/tradeoffs.md) | चार revocation architectures, JWT tension, और CVSS चर्चा |
| [docs/limitations.md](https://github.com/het-p301204/afterlife/blob/main/docs/limitations.md) | हर blind spot, fix क्या ठीक नहीं करता, audit क्या बंद करता है, और lab के अपने compromises |
| [docs/console-design.md](https://github.com/het-p301204/afterlife/blob/main/docs/console-design.md) | console mortality register क्यों है, palette और type निर्णय, और क्या हटाया गया |
| [docs/console-preview.html](https://github.com/het-p301204/afterlife/blob/main/docs/console-preview.html) | console, एक self-contained file में baked |
| [docs/figures/](https://github.com/het-p301204/afterlife/blob/main/docs/figures) | इस README के figures, payload से generated |
| [report/AFTERLIFE-report.md](https://github.com/het-p301204/afterlife/blob/main/report/AFTERLIFE-report.md) | vulnerable run के लिए एक generated incident report |
| [SECURITY.md](https://github.com/het-p301204/afterlife/blob/main/SECURITY.md) | local-only boundary, lab credentials, redaction contract |
| [evidence/](https://github.com/het-p301204/afterlife/blob/main/evidence) | sanitized sample telemetry, उससे उत्पन्न alerts, और state audit |

---

## Research references

2026-09-11 को primary sources के विरुद्ध verified। **VERIFIED** का अर्थ है कि vendor की स्वयं की advisory पढ़ी गई; **REPORTED** का अर्थ है कि विवरण vendor के बजाय किसी vulnerability database से आया है।

**Standards and classifications**

* [OWASP Top 10:2025 — A07 Authentication Failures](https://top10.owasp.org/2025/A07_2025-Authentication_Failures) — ठीक इसी विफलता का वर्णन करता है: ऐसे applications जो "correctly invalidate user sessions or authentication tokens" नहीं करते। इसकी CWE mapping में नीचे दिए गए तीनों शामिल हैं।
* [RFC 9700](https://datatracker.ietf.org/doc/rfc9700/) — *Best Current Practice for OAuth 2.0 Security* (BCP 240, January 2025)। इस lab के दो आधार: rotated refresh token का reuse चोरी का संकेत देता है (AFTERLIFE-002), और **revocation को पूरे token family को invalidate करना चाहिए, केवल वर्तमान token को नहीं** — जो AFTERLIFE-001 की thesis है, IETF practice के रूप में codified।
* [OWASP Session Management Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html) — credential और security परिवर्तनों के बाद session invalidation
* [CWE-613: Insufficient Session Expiration](https://cwe.mitre.org/data/definitions/613.html) — *"permits an attacker to reuse old session credentials or session IDs for authorization"*। नीचे दिए गए तीनों CVEs इसी class में filed हैं।
* [CWE-287: Improper Authentication](https://cwe.mitre.org/data/definitions/287.html)
* [CWE-384: Session Fixation](https://cwe.mitre.org/data/definitions/384.html) — adjacent: एक session identifier जो authentication state परिवर्तन के बाद भी जीवित रहता है

**The pattern this lab reproduces**

* **VERIFIED** — [GHSA-hvp3-26wx-g2w4](https://github.com/strapi/strapi/security/advisories/GHSA-hvp3-26wx-g2w4) / [CVE-2026-22706](https://github.com/advisories/GHSA-hvp3-26wx-g2w4), *Strapi: Password Reset Does Not Revoke Existing Refresh Sessions*. `@strapi/admin` और `@strapi/plugin-users-permissions` ≤ 5.33.2; 5.33.3 में fixed। CVSS v4.0 **2.1 (Low)**, `AV:N/AC:H/AT:N/PR:H/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N`. Published 2026-05-13.
  Vendor advisory बताती है कि refresh-token invalidation step caller-supplied `deviceId` पर conditional था, और patch हर password change तथा reset पर सभी refresh tokens को invalidate करता है, चाहे `deviceId` दिया गया हो या नहीं। वही conditional है जिसे `_revoke_for_security_change` model करता है, और वही patch है जिसे fixed mode implement करता है।
  Vendor context: [Strapi security disclosure, May 2026](https://strapi.io/blog/security-disclosure-of-vulnerabilities-cve-2025-64526-cve-2026-22599-cve-2026-22706-cve-2026-22707-and-cve-2026-27886).

**The same class, different mechanisms**

* **REPORTED** — [CVE-2026-1163](https://nvd.nist.gov/vuln/detail/CVE-2026-1163), parisneo/lollms. Password reset पर session invalidation *बिल्कुल नहीं*, 31-दिन के default session lifetime के साथ। इस bug का सरल संस्करण — और वही जिसे naive detector वास्तव में पकड़ लेगा।
* **REPORTED** — [CVE-2026-40934](https://nvd.nist.gov/vuln/detail/CVE-2026-40934), Jupyter Server ≤ 2.17.0 (fixed 2.18.0)। Cookie-signing secret एक static file में persist किया जाता है और password change पर कभी rotate नहीं होता, इसलिए reset से पहले जारी किए गए cookies उसके पार cryptographically valid बने रहते हैं। इसी परिणाम तक पहुँचने का तीसरा रास्ता: यहाँ *credential* इसलिए valid है क्योंकि *key* कभी नहीं बदली, जो [docs/tradeoffs.md](https://github.com/het-p301204/afterlife/blob/main/docs/tradeoffs.md) में Option D का failure mode है।

तीन products, तीन mechanisms — एक conditional parameter, एक missing call, एक unrotated key — एक परिणाम: reset सफल हुआ और attacker बना रहा।

---

<div align="center">

**The attacker is not detected because their request looks malicious.**

उनकी हर request well-formed थी, सही ढंग से signed थी, और एक ऐसा credential लेकर आई थी जो server ने अभी उनके लिए mint किया था।

**They are detected because a credential lineage that should have died at a security event was accepted after it.**

<br>

<sub>MIT · a local lab · nothing here is safe to deploy</sub>

</div>
टूल डाउनलोड करें
नियमगंभीरताउत्तर देता हैसक्रिय होता है
AFTERLIFE-002रीफ्रेश क्रेडेंशियल का पुनः उपयोगCRITICAL / MEDIUMक्या यह चोरी हुआ था?पुनः उपयोग के समय
AFTERLIFE-003सुरक्षा परिवर्तन पर अपूर्ण निरस्तीकरणHIGH / LOWक्या कंटेनमेंट चला?परिवर्तन के समय — किसी हमलावर की आवश्यकता नहीं
AFTERLIFE-001निरस्तीकरण-पश्चात क्रेडेंशियल वंशावली का उपयोगHIGHक्या बासी वंशावली का उपयोग किया गया?पहली स्वीकृति के समय
RULE PACK
-h
--help
-V
--version
परिदृश्यमोडघटनाएँसबसे खराबनियम सक्रियstaledormant
legitimate-onlyvulnerable19LOWAFTERLIFE-00300
legitimate-onlyfixed19–कोई नहीं00
stolen-refreshvulnerable22HIGHAFTERLIFE-001, AFTERLIFE-00310
stolen-refreshfixed23–कोई नहीं00
stolen-refresh-with-device-idvulnerable19LOWAFTERLIFE-00300
stolen-refresh-with-device-idfixed19–कोई नहीं00
multi-devicevulnerable29HIGHAFTERLIFE-001, AFTERLIFE-00320
multi-devicefixed29–कोई नहीं00
refresh-reusevulnerable11MEDIUMAFTERLIFE-00200
refresh-reusefixed15MEDIUMAFTERLIFE-00200
dormant-survivorvulnerable15HIGHAFTERLIFE-00311
dormant-survivorfixed19–कोई नहीं00
mfa-changevulnerable18HIGHAFTERLIFE-001, AFTERLIFE-00310
mfa-changefixed18–कोई नहीं00
account-recoveryvulnerable18HIGHAFTERLIFE-001, AFTERLIFE-00310
account-recoveryfixed18–कोई नहीं00
expired-lineagevulnerable17LOWAFTERLIFE-00300
expired-lineagefixed20–कोई नहीं00
मामलापरिणामक्यों
पासवर्ड परिवर्तन, फिर प्रतिस्थापन सत्र का उपयोगकोई अलर्ट नहींनई lineage का रूट वॉटरमार्क पर या उसके बाद है
रीसेट फिर तत्काल ब्राउज़िंग (/me, /profile, /settings)कोई अलर्ट नहींएक ताज़ा lineage, एक ताज़ा रूट
फ़ोन, लैपटॉप और टैबलेट, सभी सही ढंग से रोटेटेडकोई अलर्ट नहींप्रत्येक लॉगिन अपनी स्वयं की lineage है
एक डिवाइस जिसने परिवर्तन के बाद लॉगिन किया, हमलावर के साथ ब्राउज़ कर रहा हैकोई अलर्ट नहींताज़ा रूट — और stale lineage अभी भी अकेले अलर्ट करती है
एक stale क्रेडेंशियल जिसे अस्वीकार किया गयाकोई अलर्ट नहींresult: failure को बाहर रखा गया है; यह बचाव के लिए साक्ष्य है
घटकों के बीच 2s तक क्लॉक स्क्यूकोई अलर्ट नहींप्रलेखित सहनशीलता
एक विफल पासवर्ड परिवर्तनकोई अलर्ट नहींएक एंकर नहीं
परिवर्तन से पहले की गतिविधिकोई अलर्ट नहींसमय-क्रम जाँच
एक रोटेटेड क्रेडेंशियल (reason: rotated)मृत्यु के रूप में नहीं गिना जाताएक क्रेडेंशियल खर्च हुआ, एक lineage मारी नहीं गई
40 दिन पुराना सत्र जिसके क्रेडेंशियल समाप्त हो गएएक survivor नहींसमाप्ति प्रति lineage ट्रैक की जाती है
एक बिल्कुल नए खाते पर पहला पासवर्ड परिवर्तनकोई अलर्ट नहींअंदर जाते समय कुछ भी जीवित नहीं था
एक stale lineage से Refresh-token रोटेशनALERTताज़ा टाइमस्टैम्प, stale वंशावली — यही finding है
परिवर्तनक्रेडेंशियल्स अविश्वसनीय क्यों हो जाते हैं
पासवर्ड परिवर्तन / रीसेटवह रहस्य जिसके साथ सत्र स्थापित हुआ था, चला गया
MFA नामांकन या परिवर्तनजिन कारकों के साथ सत्र स्थापित हुआ था, वे खाते के कारक नहीं हैं
भूमिका परिवर्तन / विशेषाधिकार वृद्धिक्रेडेंशियल एक भिन्न प्राधिकरण के अंतर्गत बनाया गया था
खाता पुनर्प्राप्तिनिर्माण से ही, खाता एक क्षण पहले किसी और के हाथ में हो सकता है
लॉगिनअन्य को नहीं निरसित करता — एक नई वंशावली, यह कथन नहीं कि पुराने अविश्वसनीय हैं
तंत्रक्या देता हैक्या खर्च होता है
Aप्रति-उपयोगकर्ता निरसन वॉटरमार्कएक लेखन सब कुछ निरसित कर देता है, जिसमें वे क्रेडेंशियल्स भी शामिल हैं जिन्हें सर्वर भूल गया है; O(1) भंडारण और जाँचरीड पथ पर सर्वर-साइड स्थिति; टाइमस्टैम्प अर्थ ठीक-ठीक सही होना चाहिए; वॉटरमार्क के बाद जारी किए गए क्रेडेंशियल्स के बारे में कुछ नहीं कहता
Bअल्पकालिक एक्सेस + निरसनीय रीफ्रेशरीड-पथ स्थिति के बिना एक्सेस-टोकन क्षति को सीमित करता हैचोरी हुआ एक्सेस टोकन समाप्त होने तक वैध रहता है; रीफ्रेश पक्ष को अभी भी स्थिति चाहिए — यही वह पैटर्न है जिसके भीतर CVE रहता है
Cस्पष्ट डिनाइलिस्टसटीक; घटना प्रतिक्रियाकर्ता के लिए सुपाठ्य; वह टेलीमेट्री उत्पन्न करता है जो नियंत्रण साबित करती हैस्थिति बढ़ती है और सफ़ाई चाहिए; केवल उसे निरसित करता है जिसे आपने गिनना याद रखा — वही क्वेरी जिसे बग ने गलत किया
Dपूर्णतः स्टेटलेस JWTरीड-पथ स्थिति बिल्कुल नहींकोई निरसन नहीं है। TTL और कुंजी रोटेशन ही एकमात्र उपाय हैं
VariableDescription
KITPLOIT_SERVERServer URL
KITPLOIT_TOKENAPI token
KITPLOIT_FORMATDefault output format
KITPLOIT_VERBOSEEnable verbose mode (true/false)
CodeMeaning
0Success
1General error
2Invalid arguments
3Authentication failure
4Resource not found
5Server error
EndpointMethodDescription
/api/v1/scansGETList all scans
/api/v1/scansPOSTCreate a new scan
/api/v1/scans/{id}GETGet scan details
/api/v1/scans/{id}DELETEDelete a scan
/api/v1/scans/{id}/resultsGETGet scan results
/api/v1/targetsGETList all targets
/api/v1/targetsPOSTAdd a new target
CodeMeaning
0Success
1General error
2Invalid arguments
3Authentication failure
4Network error
5Scan timeout
-h
--help
CodeMeaning
0Success
1General error
2Invalid arguments
3Connection failed
4Authentication failed
-h
--help