1 पॉइंट द्वारा GN⁺ 2025-05-25 | 2 टिप्पणियां | WhatsApp पर शेयर करें
  • tachy0n iOS 13.0~13.5 पर काम करने वाला एक kernel privilege escalation exploit था, जिसे 23 मई 2020 को unc0ver v5.0.0 में 0day के रूप में शामिल किया गया और इसने उस समय के नवीनतम iOS को सीधे निशाना बनाया
  • इसका मूल lio_listio की Lightspeed race condition थी, जो kalloc.16 object को दो बार free करके अलग-अलग objects को एक ही memory की ओर point करवाने में सक्षम बनाती थी
  • इसी परिवार की bug पहले iOS 11 के Spice jailbreak/untether में भी इस्तेमाल हुई थी, लेकिन app sandbox और racoon environment में privilege, sandbox और spray techniques के लिहाज़ से बड़े फर्क थे
  • unc0ver के exploit ने बार-बार OSData overlap हासिल किया, और IOBufferMemoryDescriptorIOAcceleratorFamily2 का उपयोग करके fake task और fake mach port बनाए
  • iOS 14 के बाद Apple allocator separation, sequestering, PAC और object-specific hardening की ओर बढ़ा, यानी exploit strategy को ही ब्लॉक करने वाले मॉडल की तरफ, और सार्वजनिक iOS kernel exploit ज्ञान अब iOS 13 के दौर की तुलना में काफी पीछे है

tachy0n का सार्वजनिक होना असामान्य क्यों था

  • tachy0n एक पुराना exploit था जो iOS 13.0 से 13.5 तक प्रभावित करता था, और 23 मई 2020 को unc0ver v5.0.0 में शामिल होकर सार्वजनिक हुआ
  • उस समय के हिसाब से यह एक सामान्य kernel local privilege escalation (kernel LPE) था, लेकिन नवीनतम iOS version को प्रभावित करने वाले 0day jailbreak के रूप में इसका सार्वजनिक होना दुर्लभ था
  • Apple ने exploit के सार्वजनिक होने के लगभग 1 हफ्ते बाद इस bug के लिए समर्पित patch जारी किया
  • यह bug iOS 13.5 में 0day था, लेकिन पहले 1day के रूप में इसका उपयोग हो चुका था
  • Pwn20wnd ने app sandbox से पहुँचा जा सकने वाला 0day खोजा, और शुरुआती बिंदु ज्ञात 1day bugs पर regression testing था
    • iOS 12 का SockPuppet iOS 12.3 में patch हुआ था, लेकिन iOS 12.4 में फिर दिखा
    • इस मामले ने इस तरह की bug class पर Apple की regression testing की कमी दिखा दी, और Pwn20wnd ने कुछ ज्ञात 1day पर regression tests लागू करके hit पाया

Lightspeed: lio_listio race condition

  • tachy0n की मुख्य bug Synacktiv की Lightspeed bug थी, जो CVE-2020-9859 और CVE-2018-4344 की संभावना से जुड़ी थी
  • vulnerability lio_listio syscall में थी, जो asynchronous या batch file I/O चलाती है
  • kernel submitted I/O jobs को track करने के लिए aio_lio_context structure allocate करता है
    • इस structure में io_waiter, io_issued, io_completed fields होते हैं
  • वास्तविक काम अलग thread में होता है, और जब सभी I/O पूरे हो जाते हैं तो do_aio_completion उस context को free कर देता है
  • अगर कोई job schedule ही नहीं हुई हो, तो lio_listio का current thread context को free करता है
  • समस्या यह थी कि इस check में race condition थी
    • कोई job दूसरे thread को submit हो सकती थी, लेकिन check होने से पहले ही पूरी होकर context free हो जाता था
    • तब lio_listio dangling pointer बन चुके lio_context को फिर से check करता था

double free exploit तक कैसे पहुँचता है

  • exploit के लिए ज़रूरी sequence यह था
    • lio_listio lio_context allocate करता है
    • job पूरी होती है और do_aio_completion lio_context free कर देता है
    • attacker-controlled object उसी freed memory में reallocate किया जाता है और उसे lio_context->io_issued == 0 जैसा दिखाया जाता है
    • lio_listio इसे देखकर attacker object को दोबारा free कर देता है
    • फिर उसी memory को किसी दूसरे object के लिए reallocate किया जाता है, जिससे दो अलग allocations एक ही memory की ओर point करने लगते हैं
  • 64-bit devices पर lio_context सबसे छोटे zone kalloc.16 में जाता था
  • iOS 14 से पहले object type की परवाह किए बिना same-sized allocations एक ही allocation site share करते थे
    • C++ objects, pointer arrays और user-provided data buffers एक ही size bucket में एक-दूसरे की memory reuse कर सकते थे
  • यह double free उस सामान्य double free से अलग तरह से काम करता था जो बीच में reallocation न होने पर तुरंत fatal corruption दे देता है
    • allocation के दौरान lio_context->io_issued कभी 0 नहीं होता
    • free होने के बाद allocator पहले 8 bytes को canary value और freelist pointer या object address XOR value से overwrite करता है
    • इसलिए दूसरा free तभी होता है जब बीच में reallocation हो और bytes 4~7 शून्य हों
  • असली exploit इस race को कई बार retry कर सकता था, और system के किसी दूसरे object द्वारा संयोग से ज़रूरी bytes को 0 बनाकर double free trigger करना व्यवहार में दुर्लभ था

Spice में पहले इसका उपयोग

  • यही bug iOS 11.x को target करने वाले Spice jailbreak/untether में भी इस्तेमाल हुई थी
  • Spice पर Jake Blair team ने Sparkey और littlelailo के साथ काम किया था, और उस समय नवीनतम version iOS 13.x था
  • लक्ष्य app environment और racoon environment, दोनों में mach port forgery बनाना था
    • iOS 14 से पहले के kernel exploits में अगर user-controlled value को mach port pointer की तरह interpret करवाया जा सके, तो बाद के चरण बहुत आसान हो जाते थे
  • Lightspeed से mach port forgery बनाने का मूल flow यह था
    • lio_context का पहला free trigger करना
    • size 1 या 2 वाले OOL mach ports descriptor के साथ mach message spray करना
    • पहली entry को MACH_PORT_NULL रखकर उसे kalloc.16 में डालना और io_issued को 0 जैसा दिखाना
    • दूसरे free से OOL mach ports array को free करना
    • kalloc.16 में controlled data spray करके mach ports array को fake pointer से replace करना

app sandbox और racoon की constraints में फर्क

  • A7~A9(X) पर PAN नहीं था, इसलिए सिर्फ mmap और mlock से userland address को kernel pointer की तरह dereference कराया जा सकता था
  • A10 और A11 को भी support करने की कोशिश हुई, लेकिन app sandbox में उपयुक्त kernel address leak और controlled data placement target नहीं मिल पाने से यह पूरा नहीं हो सका
  • जिन 1day bugs का उपयोग होना था, उनमें Ian Beer का kernel stack information leak और backboardd sandbox escape शामिल था
    • योजना shared memory pointer leak करने या kernel __DATA segment में data रखने की थी
    • उपयुक्त target न मिलने के कारण app path में A10/A11 support नहीं बन पाया
  • racoon path में स्थितियाँ अलग थीं
    • यह root के रूप में चलता था, लेकिन सामान्य app की तुलना में और कड़े sandbox के साथ
    • IOSurface access न होने से सामान्य IOSUnserializeXML via IOSurface::setValue spray का उपयोग नहीं किया जा सकता था
    • इसकी जगह RootDomainUserClient::secureSleepSystemOptions के भीतर के OSUnserializeXML call से कुछ objects को leak-style spray किया जा सकता था
  • racoon के sandbox profile में सभी sysctl reads और writes की अनुमति थी, और इसे root privilege भी मिला हुआ था
    • kernel slide पता होने पर kernel __DATA में मौजूद sysctl global को ज्ञात address वाले data store की तरह इस्तेमाल किया जा सकता था
    • Spice में इसके लिए vm.swapfileprefix चुना गया
  • kernel slide प्राप्त करने के लिए panicall का CVE-2018-4413 इस्तेमाल हुआ
    • sysctl_procargsx information leak के ज़रिए kernel_map की लगभग एक page जितनी uninitialized kernel memory leak की जा सकती थी
    • इससे kernel code और heap pointer मिले, और A7~A11 को संभाला जा सका
    • app sandbox में sysctl_procargsx blocked था, इसलिए वही तरीका वहाँ संभव नहीं था

unc0ver के लिए tachy0n exploit की संरचना

  • unc0ver का target A8~A13 था, इसलिए A10+ को नज़रअंदाज़ करना या userland dereference पर निर्भर रहना संभव नहीं था
  • exploit को fail हो सकने वाले memory corruption stage को ध्यान में रखकर दो-स्तरीय संरचना में डिज़ाइन किया गया
    • निचली layer lio_listio चलाने वाले freerer threads और IOSurface के ज़रिए OSData unserialize करने वाले racer threads चलाती थी
    • default configuration freerer 4 और racer 16 थी, और इसे adjust किया जा सकता था
  • IOSurface के माध्यम से unserialize होने वाला data कई OSData entries वाला OSDictionary था
    • io_issued के अनुरूप position का 0 होना ज़रूरी था
    • 0x41414141, 0x69696969 जैसी magic values और key value k overlap detection के लिए इस्तेमाल हुईं
  • race के बाद सभी OSData values की जाँच की जाती थी
    • जिन objects की magic value बदल गई हो, उन्हें system के दूसरे object द्वारा ले लिया गया माना जाता था और बाद में cleanup target के रूप में mark किया जाता था
    • अगर key और buffer के भीतर का k value अलग हो, तो इसे इस बात का संकेत माना जाता था कि अलग OSData objects एक ही backing buffer की ओर point कर रहे हैं
  • code की maybe_reyoink और overlap functions यह overlap information बनाकर ऊपरी layer को देती थीं
  • ऊपरी layer इन overlapped OSData objects का उपयोग करके fake mach port बनाती थी
    • एक OSData को free करना
    • OOL port descriptor वाले mach message spray करना
    • दूसरे OSData को free करना
    • fake task port pointer वाला नया OSData reallocate करना

ज्ञात kernel address पर controlled data रखना

  • exploit OOL ports descriptor array में reallocated content को OSData के रूप में पढ़कर mach port का raw kernel pointer leak कर सकता था
  • आगे यह task port और IOSurfaceRoot service port address leak करने में काम आता था, लेकिन मुख्य समस्या यह थी कि controlled buffer का स्थिर kernel address विश्वसनीय रूप से कैसे मिले
  • XNU source में मिला candidate था IOMemoryDescriptor
    • इसका _ranges field IOVirtualRange array होता है, और एक single IOVirtualRange ठीक kalloc.16 में फिट होता है
    • लेकिन सामान्य IOMemoryDescriptor में अगर सिर्फ एक range हो तो heap allocation की जगह _singleRange इस्तेमाल होता है
  • IOBufferMemoryDescriptor एक अपवाद था, जो एक ही range के लिए IONew(IOAddressRange, 1) कॉल करके heap allocation करता था
  • इसे मनचाहे ढंग से allocate करके user address space में map करने के लिए सुविधाजनक जगह IOAcceleratorFamily2 का AGX interface था
    • IOGraphicsAccelerator2 में type 0 userclient खोलने पर IOAccelContext2 मिलता था
    • ::clientMemoryForType() से तीन memory descriptors map किए जा सकते थे
    • type 0 का size 0x8000 bytes था, इसलिए victim descriptor की पहचान में इसका उपयोग हुआ
  • exploit ने यह loop इस्तेमाल किया
    • IOAccelContext2 खोलना और overlapped OSData के दो objects लेना
    • एक OSData को free करना
    • पहले से खुले IOAccelSharedUserClient2 को IOConnectAddClient() से connect करना
    • बचे हुए OSData को पढ़कर जाँचना कि पहले 8 bytes page-aligned kernel pointer हैं और अगले 8 bytes 0x8000 हैं
    • अगर condition पूरी न हो तो IOAccelContext2 बंद करके दोहराना

pageable memory, fake port, zone_require

  • memory descriptor को process में map कर लेने और उसका kernel address जान लेने के बाद भी एक समस्या बची रही: memory kIOMemoryPageable के रूप में बनती थी
  • fake mach port और fake task object तक preemption बंद होने की स्थिति में पहुँचा जा सकता था, इसलिए kernel side पर उस page को fault-in करना पड़ता था
  • यह IOAccelContext2::processSidebandBuffer को अप्रत्यक्ष रूप से कॉल करने वाले IOAccelContext2::submit_data_buffers external method 2 को दो बार बुलाकर किया गया
    • shared memory की शुरुआत से 0x10 bytes आगे के structure को पढ़ा जाता था
    • पहले structure में tok == 0x100 होता था और उसे पूरी page cover करने के लिए बनाया जाता था ताकि processing दूसरी page तक जाए
    • दूसरी page में बाद में fake object data रखा जा सकता था
  • इसके बाद के चरण fake task, fake port, OOL descriptor switcheroo और arbitrary read primitive बनाने तक जाते थे
  • zone_require bypass भी ज़रूरी था
    • उस समय zone_require zone_map के बाहर की pages को अनुमति देता था और page के पहले 0x20 bytes को metadata की तरह interpret करता था
    • सही zone index भर देने पर यह इच्छित zone के लिए pass की तरह काम कर सकता था
    • इसी कारण task के लिए एक page और mach port के लिए एक page, कुल दो pages चाहिए थे
  • यह exploit अब GitHub पर सार्वजनिक है

सार्वजनिक होने के बाद analysis और patch

  • नवीनतम signed version के लिए पूर्ण 0day exploit का सार्वजनिक होना iOS jailbreak scene में खास ध्यान का विषय बना
  • उस समय Project Zero में काम कर रहे Brandon Azad ने exploit के सार्वजनिक होने के 4 घंटे के भीतर vulnerability समझ ली और Apple को सूचना दी
  • exploit के सार्वजनिक होने के 6 दिन बाद Synacktiv ने नई पोस्ट में यह संभावना रखी कि iOS 12 का मूल fix memory leak पैदा कर रहा था, और उस memory leak को ठीक करने की कोशिश ने शायद मूल bug को फिर से जीवित कर दिया
  • exploit के सार्वजनिक होने के 9 दिन बाद Apple ने patch जारी किया
  • बाद में XNU में इस bug के लिए regression test जोड़ा गया
  • सार्वजनिक होने के 54 दिन बाद reverse-engineered version “tardy0n” Odyssey jailbreak में शामिल किया गया, और इसका target भी iOS 13.0~13.5 ही था

iOS 14 के बाद exploit environment कैसे बदला

  • iOS 14 ने Apple की kernel security strategy में बदलाव दिखाया
  • iOS 14 से पहले शुरुआती primitive चाहे heap overflow हो, C++ object over-release हो, या type confusion, अगला target आम तौर पर mach port ही होता था
  • iOS 14 में सबसे बड़े बदलावों में से एक allocator kalloc और zalloc थे
    • zone map को कई “kheap” ranges में बाँटा गया
    • user-controlled data और kernel objects को अलग-अलग heaps में अलग किया गया
    • kernel objects पर sequestering लागू की गई ताकि किसी specific zone को allocated virtual address pages reboot से पहले किसी दूसरे zone के लिए reuse न हों
    • physical memory free हो सकती थी, लेकिन virtual memory range दूसरे object के लिए reuse नहीं होती थी, जिससे kernel object type confusion व्यावहारिक रूप से रुक गई
  • guard pages, हर boot पर बदलने वाले zone allocation start positions, और बाद की refinements ने cross-zone attacks की reliability को बहुत कम कर दिया
  • Apple सिर्फ individual bugs को रोकने के बजाय exploit strategy को रोकने की दिशा में गया
    • अगर exploit kmsg struct को corruption target बनाता, तो उस structure को sign कर दिया जाता
    • अगर pipe buffer को stable kernel read/write interface की तरह इस्तेमाल किया जाता, तो संबंधित pointers को PAC से सुरक्षित किया जाता
    • अगर unrelated objects को victim की तरह इस्तेमाल करने का तरीका सामने आता, तो उस object type को harden कर दिया जाता
  • नतीजतन exploit development में शुरुआती memory corruption 0day से ज़्यादा मूल्य exploit strategy का हो गया

सार्वजनिक ज्ञान में टूटन

  • iOS 14 से पहले यह आकलन था कि सार्वजनिक iOS security research का ज्ञान निजी ज्ञान के लगभग बराबर था
  • iOS 14 के बाद, कुछ अपवादों को छोड़कर, जानकारी साझा करना लगभग रुक गया
  • iOS 19 beta आने में कुछ ही हफ्ते बचे होने के बावजूद, iOS 18 या iOS 17 के लिए कोई सार्वजनिक kernel exploit उपलब्ध नहीं बताया गया
  • Apple security notes में कभी-कभी जंगली इस्तेमाल में आए vulnerabilities दिखते हैं, लेकिन सार्वजनिक जानकारी निजी research की बराबरी नहीं कर पा रही
  • यह तथ्य कि tachy0n के सार्वजनिक होने को अभी सिर्फ 5 साल हुए हैं, दिखाता है कि iOS kernel exploit का क्षेत्र कितनी तेज़ी से बदल चुका है

2 टिप्पणियां

 
ndrgrd 2025-05-26

Apple का hardware बेहतरीन है, लेकिन उसका software उपयोगकर्ताओं को बाँधकर रखने की मंशा से भरा हुआ है.
आप अपनी बनाई और build की हुई app को सिर्फ अपने ही device पर चलाना चाहें, तब भी 100 डॉलर का subscription चाहिए.

अगर आप छोटे या मध्यम स्तर के open source apps इस्तेमाल करते हैं और उन्हें खुद build करके चलाने वाले developer हैं,
तो Apple के device पर vulnerability का फायदा उठाकर jailbreak करके sideload करने से बेहतर है कि बस Android इस्तेमाल करें.

 
GN⁺ 2025-05-25
Hacker News की रायें
  • यह बात प्रभावशाली है कि 1 ट्रिलियन डॉलर की कंपनी को हराने का तरीका वही सरल और उबाऊ काम था जिसमें Apple खास तौर पर कमजोर है: regression testing
    iOS 12 में jailbreak के लिए इस्तेमाल हुई बड़ी कमजोरियों में से एक SockPuppet थी, जिसे Project Zero के Ned Williamson ने खोजकर Apple को रिपोर्ट किया था, iOS 12.3 में पैच किया गया था, और बाद में Project Zero bug tracker पर सार्वजनिक किया गया था
    लेकिन iOS 12.4 में यह ऐसे वापस आ गई जैसे कभी पैच हुई ही न हो, और शायद Apple ने उस version के लिए XNU को अलग branch में fork करते समय patch लागू नहीं किया
    यह इस बात का मजबूत संकेत था कि इस तरह की कमजोरियों के लिए regression tests नहीं थे, और कुछ ज्ञात 1-day को automate कर देने भर से Pwn सीधे hit कर सकता था
    जिज्ञासा है कि Linux, FreeBSD, OpenWRT, OpenSSH जैसे projects के नए versions पर पुरानी कमजोरियां लगातार चलाने वाले CI farms कितने लोग चला रहे होंगे

    • Regression testing एक standard QA प्रक्रिया है, जिससे यह जांचा जाता है कि ठीक किया गया bug फिर से तो नहीं आ गया
      20 साल पहले university के दिनों में मैंने Mozilla में volunteer QA किया था, और rendering/layout या JavaScript engine bugs पर केंद्रित regression tests का एक लगातार बढ़ता collection था
      reproduce करने और fix verify करने के लिए minimal test cases बनाते-बनाते, उन्हें build pipeline में डालना भी आसान था
      Bugs से बचना असंभव है, लेकिन जिस bug को ठीक करने में समय और पैसा लगा हो उसका फिर से जिंदा हो जाना सबसे खराब scenario है
      quality को महत्व देने वाले organizations निश्चित तौर पर regression testing में invest करते हैं, लेकिन कई organizations QA की कद्र नहीं करते और या तो उसे करते ही नहीं या सबसे सस्ते outsource vendor को दे देते हैं
      Apple के पास jailbreak जैसे ऐतिहासिक रूप से सबसे high-profile bug class के लिए regression tests न होना सचमुच अजीब है
      आजकल Mozilla पर कई आलोचनाएं हो सकती हैं, लेकिन 2000s की शुरुआत में भी वह Tinderbox और Bugzilla जैसे tools के साथ काफी मजबूत QA और CI/CD चला रहा था
      जब DevOps popular हुआ और इन तरीकों को mainstream बनाया, तो मुझे लगा था कि यह तो सब पहले से कर ही रहे होंगे, लेकिन वह मेरा भ्रम था
    • अगर यहां projects में intelligence agencies भी शामिल हैं, तो यह मानना सुरक्षित है कि कम से कम G10 intelligence agencies, रूस, चीन, उत्तर कोरिया और अनगिनत private groups ऐसा काम कर रहे हैं
    • मूल समस्या यह लगती है कि कई organizations ने security work को अलग flow और अलग bug classification में isolate कर दिया है
      यह security और feature development के separation से बनी Conway's law जैसी स्थिति है
      भले ही build/release process और mature regression test suite मौजूद हों, internal org structure की वजह से ऐसे security issues शायद उसके अंदर शामिल ही न हों
    • नाम याद नहीं, लेकिन मैंने एक FOSS project देखा था जिसमें हर issue के लिए test case directory थी
      आसानी से हजारों थीं, और शायद वह SQLite था
      यह अपनाने लायक तरीका है
      अगर fixes को backport नहीं किया जाता, तो tests को भी backport न किए जाने की संभावना ज्यादा लगती है
  • kheap separation, task port mitigations, SSV, SPTM जैसी बातें देखकर ऐसा लगा जैसे किसी विदेशी भाषा में दोस्त से अच्छी बातचीत चल रही हो और अचानक brain surgery या nuclear physics की व्याख्या शुरू हो जाए, और समझ एकदम खाई में गिर जाए
    blast furnace refurbishment पर हुई बातचीत को interpret करने की कोशिश करते समय भी कुछ ऐसा ही लगा था
    अफसोस है कि jailbreak अब पहले जितना active नहीं रहा
    jailbreak किए हुए iPad से मैंने व्यावहारिक तौर पर ज्यादा कुछ नहीं किया, लेकिन मजा आया था, और आज होता तो tethering app, UTM और JIT solution install करना चाहता
    SideStore भी promising लगा था, लेकिन मेरा account कभी paid Apple Developer account था, इसलिए मेरे पास 10 app IDs बची हैं जो expire नहीं होतीं, और इस वजह से नया account बनाए या फिर से पैसे दिए बिना UTM जैसे apps install नहीं कर सकता

    • मैंने पुराना iPhone 4 jailbreak करके इस्तेमाल किया था, और वही iPhone को primary device के तौर पर इस्तेमाल करने का व्यवहार में एकमात्र तरीका था
      उसे खोने के बाद मैं Android पर वापस चला गया, और तब तक Android basic features के मामले में काफी पकड़ बना चुका था
  • सुना है कि Apple आजकल jailbreak के लिए 1 million dollars देता है, और वह free-market price की lower bound होगी

    • वह सीमा 2015 में ही टूट चुकी थी, लगभग 10 साल पहले: https://www.dailymail.co.uk/sciencetech/article-3301691/New-...
    • वही market price है: https://cyberscoop.com/zerodium-android-zero-days-bounty/
    • अगर किसी के पास jailbreak है, तो जिज्ञासा है कि Apple से संपर्क करके कई million dollars का reward claim करने का कोई तरीका है या नहीं
      क्या किसी broker के जरिए जाना पड़ता है, या कोई public email है जहां first-line customer support में जाकर मामला दब न जाए
  • अगर यह सच है तो Apple ने कमाल की strategy अपनाई है
    devices पर root पाने के सारे रास्ते बंद कर दो, फिर jailbreak developers जो vulnerabilities मुफ्त में खोजते हैं, उन्हें Apple patch कर सकता है

    • लेकिन पूरी तरह ऐसा नहीं है
      लेख के मुताबिक private communities के पास अब भी exploits हैं और Apple उन्हें patch करता है
      public community या इस developer की तरफ ही किसी वजह से अब ऐसा नहीं दिखता
  • पूरे लेख में मेरी पसंदीदा line थी: “iOS 13.0 में उस bug को unpatch करने वाले किसी व्यक्ति का भी धन्यवाद करना चाहूंगा। वह भी काफी cool move था।”

  • “5 साल बाद हम कहां होंगे, इसकी कल्पना भी नहीं कर सकता” — लेकिन मैं कर सकता हूं
    iMessage अब भी devices, accounts और data takeover को संभव बनाता है

  • लेख में यह नहीं बताया गया कि यह tethered था या untethered

    • tachy0n local privilege escalation (LPE) है, इसलिए वह classification खास फिट नहीं बैठता
      जिस jailbreak unc0ver में इसे शामिल करके distribute किया गया था, वह शायद “semi-untethered” था, जहां तक मुझे याद है