Tuesday, May 30, 2023

Penetračný tester / Penetračná testerka

Miesto práce

Bratislava, Slovensko (Pozícia umožňuje aj prácu z domu)

Mzdové podmienky (brutto)

Od 2 000 EUR/mesiac plus motivačné bonusy

Finálna suma základnej zložky mzdy môže byť navýšená v závislosti od odborných skúseností vybraného kandidáta.

Druh pracovného pomeru

Plný úväzok, živnosť, alebo dohodou

Termín nástupu

Ihneď

 

Hľadáme nadšenca do nášho tímu, ktorý chce tráviť čas hackingom - hľadaním zraniteľností vo webových aplikáciách. Je jedno či ste junior alebo senior.

Ak sa pripojíte k nášmu tímu, budete mať príležitosť pracovať na rôznych projektoch pre rôznych klientov. Vaše úlohy budú zahŕňať skúmanie zabezpečenia a hľadanie zraniteľností, ktoré môžu ohroziť systémy a aplikácie klientov. Taktiež budete mať možnosť vylepšiť si svoje zručnosti v penetračnom testovaní, pretože sa budete učiť od skúsených členov tímu.

Snažíme sa rozvíjať kultúru Work-to-Live (práca pre život), a nie Live-to-Work (život pre prácu). Ľúbime to, čo robíme, ale máme radi aj osobný život. Aj keď sa stáva, že členovia nášho tímu pracujú do polnoci hľadaním zraniteľností, na súťažiach CTF alebo na osobných projektoch. Keďže máme radi aj osobný život, ponúkame Vám 4-dňový pracovný týždeň, ale o tom viac na úvodnom pohovore.

Od penetračného testovania webu sa môžete posunúť aj k hackovaniu mobilných aplikácii, infraštruktúry, cloudových a kontajnerových riešení až k reverznému inžinierstvu a exploitácii.

Nezáleží nám na vašom vzdelaní ani certifikátoch. Ak ste naozaj dobrý a máte vášeň pre počítačovú bezpečnosť a chuť sa neustále vzdelávať, ste ten správny kandidát.

 

Náplň práce:

·        Vykonávať penetračné testy webových aplikácií

·        Identifikovať a zneužiť zraniteľnosti

·        Vytvoriť záverečnú správu (report) z penetračného testu v slovenskom alebo anglickom jazyku

·        Komunikovať so zákazníkmi

·        Spolupracovať s vývojovým tímom na implementácii bezpečnostných opatrení

·        Výskum

 

Osobnostné predpoklady a zručnosti:

·        Vášeň pre počítačovú bezpečnosť

·        Chuť skúmať ako veci fungujú

·        Disciplinovanosť

·        Pochopenie etiky v podnikaní v počítačovej bezpečnosti

·        Schopnosť manuálne identifikovať a exploitovat bežné webové zraniteľnosti

·        Znalosť webových protokolov a webových architektúr / frameworkov

·        Vedieť čítať zdrojové kódy (Java, JavaScript, TypeScript, C#, PHP, ..)

·        Sledovanie nových trendov v oblasti bezpečnosti webových aplikácií

·        Byť samostatný, ale nebojácny sa spýtať kolegu ak niečomu nerozumie

 

Čo ponúkame:

·        Platená pozícia na plný úväzok, živnosť, alebo dohodou

·        Motivácia založená na kvalite a efektivite

·        Školenie a certifikácie v oblasti ofenzívnej bezpečnosti

·        Konferencie (zahraničné aj domáce)

 

Možnosť pracovať:

·        V kancelárii

·        Z domu (vzdialene, ale záleží aj od projektu)

·        Kombinácia

 

Ak máte záujem, pošlite nám svoj životopis na hr@binary.house a povedzte nám o svojich zručnostiach.

 

O spoločnosti:

Binary House je slovenská spoločnosť so sídlom v Bratislave, ktorá poskytuje služby v oblasti ofenzívnej IT bezpečnosti. Pomáha svojim klientom identifikovať a opraviť ich zraniteľné miesta v aplikáciách, sieťach a systémoch. Medzi poskytované služby patrí penetračné testovanie, bezpečnostné audity, reverzné inžinierstvo, vývoj PoC / Exploitov a útoky pomocou sociálneho inžinierstva.

Friday, April 28, 2023

ZeroPointSecurity Certified Red Team Operator (CRTO) Course - Few notes

Preface 

During the end-of-year madness with projects, I was searching for distractions for my troubled mind and saw several praising posts on Twitter about a red teaming course. Having a homie who also took part in the course and wrote a very comprehensive, although dated, blog post (https://v3ded.github.io/misc/certified-red-team-operator-crto-review) convinced me to look into it. Despite my reluctance to work within a Windows environment, I decided to take a step out of my comfort zone, so I jumped on the hype train and bought the course. The course strongly revolves around Active Directory misconfigurations leveraging Cobalt Strike. As my friends from the AV industry would say: "duh, Cobalt Strike again, boring." Their words are a true testament that probably one of the most widespread malware contains packed and obfuscated cracked versions of Cobalt Strike. Being able to emulate the tactics, techniques, and procedures (TTPs) of real-world APT groups—that's what red teaming is really about, isn't it? 

Labs and course material 

All of the materials are hosted on Canvas in the form of short Markdown articles. From the very beginning of the course, the author guides you through topics about the specifics of red teaming, especially in comparison with penetration testing. These mini-articles are clear and concise, which is good on the one hand, but on the other hand, some of the modules are so sparse that someone with no experience in red teaming will have a hard time materializing the concepts. There are also sections where the author does not care about explaining underlying concepts, which are left to the astute reader. For this reason, I would not recommend this course for beginners in IT security, as many topics are very light in terms of theory, and the section just shows that you carry out this specific attack in this way and that's it. If you blindly follow the commands for carrying out an attack, you will have a tough time in the exam. I've seen many students struggling with basic concepts and techniques, which Cobalt Strike allows you to perform in a single click. It's a double-edged sword: anyone can execute the attacks, but if the concept is not understood well, you just don't know when or how to use it properly. Many students didn't realize what the pivoting or session passing are really good for. As I mentioned, the modules are very straightforward and give you step-by-step instructions to execute an attack. I must confess that it is a very different approach to learning, as OffSec encourages in their courses. There are very few explicitly stated challenges that could push your critical thinking. On the flip side, you should pay close attention to every sentence, as many of the modules contain little nudges that hide a treasure that can be leveraged in the exam. Throughout the course, you will go through each stage of the attack lifecycle—from initial compromise to full domain takeover, data hunting, and exfiltration. You will also learn how common "OPSEC failures" can lead to detection by defenders and how to carry out those attacks in a more stealthy way. This is a huge feature, as the course allows you to check all the metadata and events that were triggered by your attack in the Splunk instance. When you have exhausted all of your ideas for replicating the attack, there is a very active and helpful Discord channel for the course available to all who participate in the course, where you can ask questions. 

Exam 

The exam is a 48 hour, hands-on CTF, carried as an assumed breach, where you attack several forests.   You are required to get at least 6 out of 8 flags to pass. The exam is, however, available for 4 days or 48 lab hours (whatever expires first), and it's possible to pause it if you want to take a break. The difficulty of the exam was fair and everything you need to successfully pass is in the course, even without annoying proctoring (unlike OffSec). The course teaches you techniques and attacks within latently protected environment (e.g. no antivirus and firewall). However, it is no secret that exam lab is protected by Defender with AMSI and a firewall, which will probably give you headaches. Therefore, after you scramble through the lab once, it's recommended to turn the Defender on and rinse and repeat the modules one more time. In my experience, I was very impatient with executing the commands and beacons were very often killed by behavioral analysis. During the exam I had several connection issues and the whole exam lab was so slow. The only support is the author of the course, so if you experience an issue and he is currently offline, you are left in the dark alone. 

Costs 

When it comes to value for money, there are only few courses that could beat this one to the punch. All in all, for £399.00, you will get lifetime access to the course materials, which seems to be fairly updated every now and then, 40 hours in the labs (can be extended for another 40 hours for 20£), that includes all the important server instances that you find in enterprise environments and hands-on experience with the premiere redteaming framework Cobalt Strike. 

Conclusion 

It should be noted that the course is a rated as beginner-intemediate course and thus, the Defender isn't fully up to date, even though the servers are Windows Servers 2022, but I witnessed multiple times during engagements that the many attacks carry over as they are of misconfigurations nature, rather than straight vulnerabilities that are being patched. After all, I would recommend this course after taking OSCP as a logical step towards the beauty of the red teaming world.

Monday, October 8, 2018

Sneak peek under the hood of Electra's kexecute


Few months ago, a jailbreak called Electra (1) has been publicly released. The jailbreak targets iOS 11.2 - 11.3.1 with astonishing kernel exploits (2-3) provided by Ian Beer of Google's Project Zero.

The team behind Electra combines and packs multiple publicly known techniques in order to achieve goals to weaken the security mechanisms and run unsigned code on the device.

This short blog post does not provide any novel techniques but it rather describes internals of Electra's kexecute function that serves a limited way to call kernel functions from the userland.

Actually, we wanted to compare it with kexecute provided by Qilin (4) (post-exploitation framework) but at the time of writing this post, Qilin exports the kexecute, however the .o library does not contain any implementation (shasum: fabc6a8d99b1c2cdd35277533b003f630fd2a2bf).

During the post-exploitation phase, we find ourselves in the position, when calling a kernel function can save us from the complicated situations. Simulating function calls with raw arbitrary r/w capability can be really daunting task, so let the kernel work for us!

This technique has been published in the "Tales from iOS 6 Exploitation and iOS 7 Security Changes" by Stefan Esser (5).

First of all, we need a convenient way to pass our user-supplied arguments to the kernel, that's where IOKit comes to the rescue.

kern_return_t
IOConnectTrap6(io_connect_t connect,
        uint32_t  index,
        uintptr_t p1,
        uintptr_t p2,
        uintptr_t p3,
        uintptr_t p4,
        uintptr_t p5,
        uintptr_t p6 )
{
    return iokit_user_client_trap(connect, index, p1, p2, p3, p4, p5, p6);
}

IOConnectTrap6(...) allows a user to send up to 6 arguments to the kernelland. The function immediately calls iokit_user_client_trap(...). 

The iokit_user_client_trap(...) function returns a value from the function called upon the IOExternalTrap instance.

The IOExternalTrap is descibed by the following structure:

struct IOExternalTrap {
 IOService * object;
 IOTrap func;
};

Subsequently, the iokit_user_client_trap(...) function calls the trap function with the user-supplied arguments.

kern_return_t iokit_user_client_trap(struct iokit_user_client_trap_args *args)
{
    kern_return_t result = kIOReturnBadArgument;
    IOUserClient *userClient;

    if ((userClient = OSDynamicCast(IOUserClient,
            iokit_lookup_connect_ref_current_task((OSObject *)(args->userClientRef))))) {
        IOExternalTrap *trap;
        IOService *target = NULL;

        trap = userClient->getTargetAndTrapForIndex(&target, args->index);

        if (trap && target) { // [A]
            IOTrap func;

            func = trap->func;

            if (func) {
                result = (target->*func)(args->p1, args->p2, args->p3, args->p4, args->p5, args->p6);
            }
        }

 iokit_remove_connect_reference(userClient);
    }

    return result;
}

Can we control the target and trap returned by getTargetAndTrapForIndex(...)?

From the following code snippet is obvious, that if we could subvert the getExternalTrapForIndex(...) of the IOUserClient instance, we are immediately in control of the *targetP and returned trap, hence we control the called function pointer (target->*func).

IOExternalTrap * IOUserClient::
getTargetAndTrapForIndex(IOService ** targetP, UInt32 index)
{
      IOExternalTrap *trap = getExternalTrapForIndex(index);

      if (trap) {
              *targetP = trap->object;
      }

      return trap;
}

The generic idea behind calling arbitrary functions, is to modify the vtable of the IOUserClient instance so that invoking the iokit_user_client_trap(...) ends up calling a function pointer controlled by us. Regarding the fact that the IOUserClient instances are backed by heap memory and behave as C++ objects stored in the memory, thus the very first address stored in this representation of instances in the memory is an address of the vtable. The vtable consists of all virtual functions that can be called on the particular instance of the objects. The other thing, that we need to take into account is the calling convention of methods, which requires us to put the "this" pointer into very first argument. The pointer points to the instance of the object itself. Note that this cannot be NULL because of the statement at [A].

The addresses of the IOUserClient instances are stored in the port member called ip_kobject, which is confirmed when we take a closer look at the function iokit_lookup_connect_ref(...) from the first code snippet:

EXTERN io_object_t
iokit_lookup_connect_ref(io_object_t connectRef, ipc_space_t space)
{
 io_object_t obj = NULL;

 if (connectRef && MACH_PORT_VALID((mach_port_name_t)connectRef)) {
  ipc_port_t port;
  kern_return_t kr;

  kr = ipc_object_translate(space, (mach_port_name_t)connectRef, MACH_PORT_RIGHT_SEND, (ipc_object_t *)&port);

  if (kr == KERN_SUCCESS) {
            assert(IP_VALID(port));
            
            if (ip_active(port) && (ip_kotype(port) == IKOT_IOKIT_CONNECT)) {
                obj = (io_object_t) port->ip_kobject;
                iokit_add_reference(obj);
            }
            
            ip_unlock(port);
  }
 }

 return obj;
}

Let's look how the team behind Electra deals with the implementation. Please note, that the code is shrank, so that the only important lines are shown.

mach_port_t prepare_user_client(void) {
...
  io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOSurfaceRoot"));

  err = IOServiceOpen(service, mach_task_self(), 0, &user_client);
...
  return user_client;
}

The function prepare_user_client() spawns an instance of IOSurfaceRoot and returns its address. 

Closer look at the following function reveals the actual implementation of the desired functionality.

void init_kexecute(void) {
    user_client = prepare_user_client();

    // From v0rtex - get the IOSurfaceRootUserClient port, and then the address of the actual client, and vtable
    IOSurfaceRootUserClient_port = find_port(user_client); // UserClients are just mach_ports, so we find its address

    IOSurfaceRootUserClient_addr = rk64(IOSurfaceRootUserClient_port + offsetof_ip_kobject); // The UserClient itself (the C++ object) is at the kobject field

    uint64_t IOSurfaceRootUserClient_vtab = rk64(IOSurfaceRootUserClient_addr); // vtables in C++ are at *object

    // The aim is to create a fake client, with a fake vtable, and overwrite the existing client with the fake one
    // Once we do that, we can use IOConnectTrap6 to call functions in the kernel as the kernel

    // Create the vtable in the kernel memory, then copy the existing vtable into there
    fake_vtable = kalloc(fake_kalloc_size);

    for (int i = 0; i < 0x200; i++) {
        wk64(fake_vtable+i*8, rk64(IOSurfaceRootUserClient_vtab+i*8));
    }

    // Create the fake user client
    fake_client = kalloc(fake_kalloc_size);

    for (int i = 0; i < 0x200; i++) {
        wk64(fake_client+i*8, rk64(IOSurfaceRootUserClient_addr+i*8));
    }

    // Write our fake vtable into the fake user client
    wk64(fake_client, fake_vtable);

    // Replace the user client with ours
    wk64(IOSurfaceRootUserClient_port + offsetof_ip_kobject, fake_client);

    // Replace IOUserClient::getExternalTrapForIndex with our ROP gadget (add x0, x0, #0x40; ret;)
    wk64(fake_vtable+8*0xB7, find_add_x0_x0_0x40_ret());

}

The find_port(user_client) looks up the specific address of the port in our task. The actual address of the IOSurfaceRootUserClient instance is grabbed from the kobject member. From now on, there are multiple venues that can get authors to the objective. One of the venues includes faking an object with a fake vtable, which is subsequently written in the the kobject member.

The team could rewrite the vtable record of getExternalTrapForIndex(index) of a spawned fake object with a function that would return a controlled value (e.g. getRegistryEntryID(), ...). The value is controlled because of the arbitrary r/w capability. 

Another way, is to replace the getExternalTrapForIndex(index) function with a gadget that would advance a controlled address to the place, where they can safely create an IOExternalTrap object.

The authors went with the latter. Firstly, a fake vtable is initialized in the heap. Afterward, up to 0x200 records from the original vtable of the IOSurfaceRootUserClient are copied into the fake vtable. A fake object of the fake_kalloc_size size is initialised. The 0x200 properties (including vtable address, refcount, etc.) from the original object are copied into the fake one. The address of fake vtable is written to the fake object. The object is now ready to be written into the kobject member.

The vtable record of the method IOUserClient::getExternalTrapForIndex is rewritten with an address of the "add x0, x0, #0x40; ret;" gadget.

Let's recapitulate the situation, what happens when we fire up iokit_user_client_trap(...). The kernel will looks up our fake object instance and calls the gadget. Do you remember what's the first parameter (stored in x0) of the call? Yes, that's the address of our fake object with the controlled data. The gadget will advance the value of x0 #0x40 upfront, so that it skips the vtable, refcount, etc because we do not want to screw that up. The advanced address of x0 is actually treated as the address of IOExternalTrap object. The goal is to fake IOExternalTrap object at that address.

The member called target would point the address of the object upon which the function is going to be called. However, in our case this represents the first argument sent to the function.

The "func" member of this object would be an address of the function that the kernel calls.

The following function called kexecute(...) creates a fake IOExternalTrap object, where the x0 represents the "this" pointer and the addr represents function that the kernel would call. From now on, the IOConnectTrap6(...) provides us a limited way to call almost arbitrary functions. The limitation comes from the restriction of the first arguments to be not NULL, the maximum number of parameters is 7 and the return value is truncated to 32bits. Besides that, the function fix up the members that were tampered with. 

uint64_t kexecute(uint64_t addr, uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6) {

    uint64_t offx20 = rk64(fake_client+0x40);
    uint64_t offx28 = rk64(fake_client+0x48);
    wk64(fake_client+0x40, x0);
    wk64(fake_client+0x48, addr);
    uint64_t returnval = IOConnectTrap6(user_client, 0, (uint64_t)(x1), (uint64_t)(x2), (uint64_t)(x3), (uint64_t)(x4), (uint64_t)(x5), (uint64_t)(x6));
    wk64(fake_client+0x40, offx20);
    wk64(fake_client+0x48, offx28);

    pthread_mutex_unlock(&kexecute_lock);

    return returnval;
}

The term_kexecute writes back the original address of IOSurfaceRootUserClient and cleans the fake object with the fake vtable out of the memory.

void term_kexecute(void) {
    wk64(IOSurfaceRootUserClient_port + offsetof_ip_kobject, IOSurfaceRootUserClient_addr);
    kfree(fake_vtable, fake_kalloc_size);
    kfree(fake_client, fake_kalloc_size);
}

If you would like to know how this technique can be boosted utilizing JOP chains to introspect the kernel (at least on older devices), please check the wonderful blog post https://bazad.github.io/2017/09/live-kernel-introspection-ios/. 

We would like to thank Brandon Azad for reviewing this blog.

References: 



Krátky pohľad na implementáciu kexecute v jailbreaku Electra


Pred niekoľkými mesiacmi bol zverejnený jailbreak nazvaný Electra (1). Jailbreak je vyvinutý pre iOS verzie 11.2 - 11.3.1 s využitím kernel exploitov (2-3), ktoré zverejnil Ian Beer z Project Zero spoločnosti Google.

Tím za jailbreakom Electra kombinuje a elegantne využíva viacero verejne známych techník, aby dosiahol oslabenie bezpečnostných mechanizmov a spustenie nepodpísaného kódu na zariadení.

Tento krátky príspevok neobsahuje žiadne nové techniky, ale skôr podrobne opisuje implementáciu kexecute, ktorá slúži na obmedzené, no za to efektívne volania funkcií jadra z tzv. userspace.

Pôvodne sme chceli porovnať Electra kexecute s kexecute poskytovanou Qilinom (4) (známy post-exploitačný framework) avšak v čase písania tohto príspevku, Qilin exportuje funkciu kexecute, avšak knižnica neobsahuje jej implementáciu (shasum: fabc6a8d99b1c2cdd35277533b003f630fd2a2bf).

Počas post-exploitácií sa ocitáme v pozícii, keď priame volanie funkcií jadra nás môže zachrániť pred komplikovanými situáciami. Simulácia volaní kernel funkcií za pomoci schopnosti čítať a zapisovať ľubovoľné hodnoty na ľubovoľné adresy môže byť naozaj komplikovanou úlohou. Cieľom je nechať jadro pracovať pre nás!

Táto technika bola uverejnená v prednáške "Tales from iOS 6 Exploitation and iOS 7 Security Changes" od Stefana Essera (5).

V prvom rade potrebujeme pohodlný spôsob, ako preniesť argumenty poslané používateľom do jadra. Pozrime sa teda, čo nám poskytne IOKit framework.

kern_return_t
IOConnectTrap6(io_connect_t connect,
        uint32_t  index,
        uintptr_t p1,
        uintptr_t p2,
        uintptr_t p3,
        uintptr_t p4,
        uintptr_t p5,
        uintptr_t p6 )
{
    return iokit_user_client_trap(connect, index, p1, p2, p3, p4, p5, p6);
}

IOConnectTrap6(...) umožňuje používateľom zaslať až 6 argumentov do kernelland. Funkcia automaticky vyvolá iokit_user_client_trap(...).

Funkcia iokit_user_client_trap(...) vracia návratovú hodnotu z funkcie zavolanej nad inštanciou IOExternalTrap, ktorá je popísaná nasledujúcou štruktúrou:

struct IOExternalTrap {
 IOService * object;
 IOTrap func;
};

Následne iokit_user_client_trap(...) funkcia vyvolá trap funkciu s argumentmi, ktoré sú zaslané používateľom.

kern_return_t iokit_user_client_trap(struct iokit_user_client_trap_args *args)
{
    kern_return_t result = kIOReturnBadArgument;
    IOUserClient *userClient;

    if ((userClient = OSDynamicCast(IOUserClient,
            iokit_lookup_connect_ref_current_task((OSObject *)(args->userClientRef))))) {
        IOExternalTrap *trap;
        IOService *target = NULL;

        trap = userClient->getTargetAndTrapForIndex(&target, args->index);

        if (trap && target) { // [A]
            IOTrap func;

            func = trap->func;

            if (func) {
                result = (target->*func)(args->p1, args->p2, args->p3, args->p4, args->p5, args->p6);
            }
        }

 iokit_remove_connect_reference(userClient);
    }

    return result;
}

Môžme kontrolovať target a trap, ktoré sú vrátené z funkcie getTargetAndTrapForIndex(...)?

Z nasledujúceho urývku zdrojového kódu je jasné, že ak sa nám podarí nahradiť funkcia getExternalTrapForIndex(...) inštancie IOUserClient, inou, nami kontrolovanou funkciou, máme pod kontrolou *targetP a navrátený trap. Z nami ovládaného trapu vieme kontrolovať ukazovateľ na funkciu (target->*func), ktorú kernel zavolá.

IOExternalTrap * IOUserClient::
getTargetAndTrapForIndex(IOService ** targetP, UInt32 index)
{
      IOExternalTrap *trap = getExternalTrapForIndex(index);

      if (trap) {
              *targetP = trap->object;
      }

      return trap;
}

Generická metóda volania ľubovoľných funkcií je modifikovať vtable IOUserClient inštancie tak, že vyvolanie iokit_user_client_trap(...) nakoniec zavolá nami kontrolovaný ukazovateľ na funkciu. Vzhľadom na to, že inštancie IOUserClient sú alokované na halde a správajú sa v pamäti ako C++ objekty, prvou adresou C++ inštancií uložených v pamäti je adresa takzvanej vtable. Vtable obsahuje záznamy adries všetkých virtuálnych funkcií, ktoré môžu byť nad inštanciou zavolané. Okrem toho konvencia volaní metód vyžaduje aby prvý argument zaslaný do volanej funkcie bol ukazovateľ "this". Tento ukazovateľ odkazuje na samého seba. Podľa if podmienky [A] vidíme, že tento argument nemôže byť NULL.

Adresy IOUserClient inštancií sú uložené v portoch respektíve v ich ip_kobject, čo je možné vidieť ak sa bližšie pozrieme na implementáciu iokit_lookup_connect_ref(...) z predchádzajúceho úryvku:

EXTERN io_object_t
iokit_lookup_connect_ref(io_object_t connectRef, ipc_space_t space)
{
 io_object_t obj = NULL;

 if (connectRef && MACH_PORT_VALID((mach_port_name_t)connectRef)) {
  ipc_port_t port;
  kern_return_t kr;

  kr = ipc_object_translate(space, (mach_port_name_t)connectRef, MACH_PORT_RIGHT_SEND, (ipc_object_t *)&port);

  if (kr == KERN_SUCCESS) {
            assert(IP_VALID(port));
            
            if (ip_active(port) && (ip_kotype(port) == IKOT_IOKIT_CONNECT)) {
                obj = (io_object_t) port->ip_kobject;
                iokit_add_reference(obj);
            }
            
            ip_unlock(port);
  }
 }

 return obj;
}

Pozrime sa na konkrétnu implementáciu v podaní Electra tímu. Upozorňujeme, že kód je len úryvok a sú znázornené iba dôležité riadky.

mach_port_t prepare_user_client(void) {
...
  io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOSurfaceRoot"));

  err = IOServiceOpen(service, mach_task_self(), 0, &user_client);
...
  return user_client;
}

Funkcia prepare_user_client() vytvorí IOSurfaceRoot inštanciu a vráti jej adresu. 
Bližší pohľad na nasledujúcu funkciu odhaľuje skutočnú implementáciu požadovanej funkcionality.

void init_kexecute(void) {
    user_client = prepare_user_client();

    // From v0rtex - get the IOSurfaceRootUserClient port, and then the address of the actual client, and vtable
    IOSurfaceRootUserClient_port = find_port(user_client); // UserClients are just mach_ports, so we find its address

    IOSurfaceRootUserClient_addr = rk64(IOSurfaceRootUserClient_port + offsetof_ip_kobject); // The UserClient itself (the C++ object) is at the kobject field

    uint64_t IOSurfaceRootUserClient_vtab = rk64(IOSurfaceRootUserClient_addr); // vtables in C++ are at *object

    // The aim is to create a fake client, with a fake vtable, and overwrite the existing client with the fake one
    // Once we do that, we can use IOConnectTrap6 to call functions in the kernel as the kernel

    // Create the vtable in the kernel memory, then copy the existing vtable into there
    fake_vtable = kalloc(fake_kalloc_size);

    for (int i = 0; i < 0x200; i++) {
        wk64(fake_vtable+i*8, rk64(IOSurfaceRootUserClient_vtab+i*8));
    }

    // Create the fake user client
    fake_client = kalloc(fake_kalloc_size);

    for (int i = 0; i < 0x200; i++) {
        wk64(fake_client+i*8, rk64(IOSurfaceRootUserClient_addr+i*8));
    }

    // Write our fake vtable into the fake user client
    wk64(fake_client, fake_vtable);

    // Replace the user client with ours
    wk64(IOSurfaceRootUserClient_port + offsetof_ip_kobject, fake_client);

    // Replace IOUserClient::getExternalTrapForIndex with our ROP gadget (add x0, x0, #0x40; ret;)
    wk64(fake_vtable+8*0xB7, find_add_x0_x0_0x40_ret());

}

Funkcia find_port(user_client) vyhľadá adresu portu v našom tasku. Adresa IOSurfaceRootUserClient inštancii je vyextrahovaná z kobject. Od tohto momentu, existuje viacero možností, ktoré môžu dostať autorov k cieľu.

Tím mohol prepísať vtable záznam getExternalTrapForIndex(...) falošného objektu s adresou funkcie, ktorá by vrátila nimi kontrolované hodnoty (napr. getRegistryEntryID(), ...). Hodnota je kontrolovaná vzhľadom na schopnosť prepisovať ľubovoľné hodnoty na ľubovoľnom mieste.

Ďalšou možnosťou je nahradiť getExternalTrapForIndex(index) tzv. gadgetom, ktorý posunie kontrolovanú adresu v registri na miesto, kde môžu vytvárať ľubovoľné dáta bez toho aby sa niečo pokazilo.

Táto metóda je využitá v Electre. Najprv, je v hlade vytvorená a inicializovaná falošná vtable. Následne až 0x200 záznamov z pôvodnej vtable IOSurfaceRootUserClient inštancie je skopírovaných do falošnej vtable. Falošná inštancia o veľkosti _kalloc_size je inicializovaná na halde. Až 0x200 členov originálnej inštancie (vrátane adresy vtable address, refcount, atď..)  je skopírovaných do falošnej inštancii. Adresa falošnej vtable je zapísaná do falošnej inštancii. Takto vytvorená inštancia je následne zapísaná do kobject v tasku.

Vtable záznam metódy IOUserClient::getExternalTrapForIndex je prepísaný adresou, kde sa nachádza gadget "add x0, x0, #0x40; ret;".

Pozrime sa, čo sa stane ak zavoláme iokit_user_client_trap(...). Kernel vyhľadá našu falošnú inštanciu a zavolá nad ňou spomenutý gadget. Pamätáte si čo je prvý parameter zaslaný do metódy(momentálne uložený v x0)? Samozrejme sa jedná o adresu nášho falošného objektu s kontrolovateľnými dátami. Gadget posunie adresu v x0 #0x40 dopredu, tak, že x0 preskakuje miesta, ktoré nemôžeme pokaziť ako napr. adresa vtable, refcount, etc. Táto adresa uložená v x0 je následne použitá ako adresa objektu IOExternalTrap. Cieľom je vytvoriť falošný IOExternalTrap objekt na tejto adrese.

Člen nazvaný target bude ukazovať na objekt nad ktorým bude metóda volaná. V našom prípade je tento člen použitý ako implicitne zaslaný prvý argument volanej metódy.

Člen "func" bude ukazovať na adresu, ktorú kernel zavolá.

Nasledujúca funkcia kexecute(...) vytvára takýto falošný IOExternalTrap objekt, kde x0 reprezentuje "this" ukazovateľ a addr reprezentuje adresu funkcie, ktorú kernel zavolá.

IOConnectTrap6(...) nám týmto spôsobom poskytuje možnosť volať takmer ľubovoľnú funkciu z kernelu s určitými obmedzeniami. Limitácie sú nasledujúce:  prvý argument nemôže byť NULL, maximálny počet zaslaných argumentov je 7, návratová hodnota je zoseknutá na 32 bitov.

Okrem toho, táto funkcia upravuje členy, ktoré boli zmenené na originálne.

uint64_t kexecute(uint64_t addr, uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6) {

    uint64_t offx20 = rk64(fake_client+0x40);
    uint64_t offx28 = rk64(fake_client+0x48);
    wk64(fake_client+0x40, x0);
    wk64(fake_client+0x48, addr);
    uint64_t returnval = IOConnectTrap6(user_client, 0, (uint64_t)(x1), (uint64_t)(x2), (uint64_t)(x3), (uint64_t)(x4), (uint64_t)(x5), (uint64_t)(x6));
    wk64(fake_client+0x40, offx20);
    wk64(fake_client+0x48, offx28);

    pthread_mutex_unlock(&kexecute_lock);

    return returnval;
}

Funkcia term_kexecute zapisuje naspať pôvodnú adresu inštancie IOSurfaceRootUserClient a uvoľnuje falošný objekt s falošnou vtable z pamäte.

void term_kexecute(void) {
    wk64(IOSurfaceRootUserClient_port + offsetof_ip_kobject, IOSurfaceRootUserClient_addr);
    kfree(fake_vtable, fake_kalloc_size);
    kfree(fake_client, fake_kalloc_size);
}

Ak by ste chceli vedieť ako táto technika môže byť vylepšená a použitá na skúmanie jadra za pomoci JOP chains, odporúčame prečítať si blog dostupný na https://bazad.github.io/2017/09/live-kernel-introspection-ios/.

Ďakujeme Brandovoni Azadovi za revíziu blogu a následné usmernenie.

Odkazy:

(1) https://coolstar.org/electra/
(2) https://bugs.chromium.org/p/project-zero/issues/detail?id=1564
(3) https://bugs.chromium.org/p/project-zero/issues/detail?id=1558
(4) http://newosxbook.com/QiLin/
(5) https://conference.hitb.org/hitbsecconf2013kul/materials/D2T2 - Stefan Esser - Tales from iOS 6 Exploitation and iOS7 Security Changes.pdf


Wednesday, July 11, 2018

Import nastavení v ESET Cyber Security PRO - zraniteľnosť alebo funkcionalita?


Na web stránke spoločnosti ESET propagujúcej ich produkt Cyber Security PRO je možné nájsť nasledujúce tvrdenie:
Užívajte si bezpečnosť s prednastavenou konfiguráciou, alebo ju vylaďte k dokonalosti s viac ako 150 nastaveniami.“ (1).

Je prednastavená (štandardná) konfigurácia skutočne bezpečná? V tomto blogu sa pozrieme na to bližšie. Prednastavená konfigurácia, respektíve inštalácia ESET Cyber Security PRO, umožňuje exportovať a importovať nastavenia Vášho antivírusového programu.

Export nastavení

 
Export nastavení

Exportovaný súbor s nastaveniami obsahuje skomprimované dáta ako je to uvedené v nasledujúcom výpise.

$ binwalk foo
DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             gzip compressed data, from Unix, last modified: 2018-07-04 12:58:30 

V tomto archíve sa nachádzajú súbory esets.xml a gui.cfg.

Súbor esets.xml


Esets.xml je štandardný súbor typu XML a obsahuje mnoho nastavení, medzi ktorými je možné nájsť záznam s názvom "plainlogs": 

<ITEM NAME="plainlogs">
     <NODE NAME="cls" TYPE="number" VALUE="77" />
     <NODE NAME="enabled" TYPE="number" VALUE="0" />
     <NODE NAME="format" TYPE="number" VALUE="1" />
     <NODE NAME="path" TYPE="string" VALUE="/var/log/esets" />
</ITEM>

Tento záznam popisuje cestu, kam sa ukladajú logy generované v prípade výskytu rôznych udalostí. Túto hodnotu dokážeme vymeniť za inú ľubovoľnú cestu. 

Import nastavení


V momente, keď importujete komprimovaný súbor, (privilegovaný) esets_daemon rozbalí archív do tmp priečinka a odtiaľ prepíše dočasné súbory do aktuálnych nastavení:

sh-3.2# ./filemon -c

187 esets_daemon      Created dir    /private/tmp/esets_import.2529790168    
768   Created         /private/tmp/esets_import.2529790168/esets.xml  
768   Chowned         /private/tmp/esets_import.2529790168/esets.xml  
768   Changed stat    /private/tmp/esets_import.2529790168/esets.xml  
768   Modified        /private/tmp/esets_import.2529790168/esets.xml  
187 esets_daemon      Created        /Library/Application Support/ESET/esets/cache/data/settings.json.mykZG6 
187 esets_daemon      Modified       /Library/Application Support/ESET/esets/cache/data/settings.json.mykZG6 
187 esets_daemon      Renamed        /Library/Application Support/ESET/esets/cache/data/settings.json.mykZG6  /Library/Application Support/ESET/esets/cache/data/settings.json

Vytvorenie POC (upraveného) súboru


Upravený súbor Esets.xml definuje novú cestu, kam budú zapisovné logy a to konkrétne "/var/db/sudo/$user/" ($user je aktuálne prihlásený používateľ v admin skupine) a tak po každej udalosti, ktorá je zalogovaná (napríklad zachytená vzorka) daemon vytvára resp. upravuje súbory nami zvolenej ceste (nezabúdajme, že tento daemon je privilegovaný).

Po vykonaní spomenutej zmeny cesty, kde sa budú vytvárať logy, je potrebné zabaliť súbory esets.xml spolu s eset.gui do archívu, ktorý bude akceptovaný antivírusovým programom.

Opätovné zabalenie je možné vykonať pomocou:  

tar czf bar.tgz esets.xml gui.cfg

Príkaz sudo a používateľ root v macOS


Používateľ prihlásený pomocou konta, ktoré sa nachádza v admin skupine (štandardná konfigurácia), môže použiť príkaz sudo v aplikácii Terminal na vykonanie príkazov v mene iného používateľa (napríklad v mene používateľa root). Po zadaní príkazu sudo, Terminal vyzve používateľa na zadanie hesla účtu. (2)

Toto opatrenie by malo chrániť pred útočníkmi s fyzickým prístupom k odomknutému zariadeniu pokúšajúcimi sa vykonať akcie v mene iného používateľa alebo vykonať systémové zmeny (v mene používateľa root).

Správcovia systému Mac môžu používať používateľský účet root na vykonávanie úloh vyžadujúcich prístup do viacerých oblastí systému.

Používateľský účet s názvom "root" je superuser s oprávneniami na čítanie a zápis do viacerých oblastí systému, vrátane súborov v ostatných používateľských účtoch v macOS.

Používateľ root nie je určený na rutinné používanie. Jeho oprávnenia umožňujú zmeny súborov, ktoré váš Mac vyžaduje. (3) 

Adresár /var/db/sudo/$user/ obsahuje časovú pečiatku, ktorá je kontrolovaná keď sa používateľ pokúša vykonať akcie v mene iného používateľa pomocou sudo. Implementácia kontroly časovej pečiatky má zmysel pre takzvanú "grace period", čo v preklade znamená, že po opätovnom vykonaní príkazov cez sudo v určitom časovom horizonte (niekoľko minút), nemusíte zadávať heslo viac krát po sebe. 

Exploitácia


Po úspešnom importovaní POC súboru, ESET Cyber Security PRO v prípade udalosti (napr. zachytenie škodlivej vzorky) vytvára súbory v nami zvolenom adresári:

187 esets_daemon      Created        /private/var/db/sudo/$user/threatslog.txt
187 esets_daemon      Modified       /private/var/db/sudo/$user/threatslog.txt

Keďže esets_daemon modifikoval obsah adresára, aktualizovala sa aj jeho časová pečiatka.
Od tohto momentu máme niekoľko minút na to, aby sme mohli vykonať sudo su, bez toho aby si od nás operačný systém vypýtal heslo.



Prevencia


Prevenciou je odstránenie používateľa z privilegovanej skupiny, čo v podstate znemožní priamy import a export nastavení bez zadania hesla.

Zmazanie používateľa z privilegovaného módu

Záver


Vyjadrenia spoločnosti ESET k tomuto prípadu boli rozpačité, nahlasená zranitelnosť bola najskôr akceptovaná a potvrdená, ale neskôr ju označili za neplatnú a výsledkom našej diskusie bolo to, že túto "funkcionalitu" nebudú opravovať. Tento prípad by mal poukázať na to, ako zlý dizajn aplikácie môže v skutočnosti uškodiť a nie pomôcť bezpečnosti. Exploitácia tohto problému vyžaduje veľa interakcie a fyzický prístup a preto dopad tohto problému môžeme označiť za nízky.

Funkcionalita bola otestovaná na produkte ESET CYBER SECURITY PRO v. 6.6.300.1 (rovnakú funkcionalitu pravdepodobne obsahuje aj ESET Endpoint Antivirus for macOS and ESET Endpoint Security for macOS).

Odkazy


(1) https://www.eset.com/sk/internetova-ochrana-domacnosti/cyber-security/
(2) https://support.apple.com/en-us/ht
020325
(3) https://support.apple.com/en-us/ht204012