Thursday, January 22, 2015

Exploiting NVMAP to escape cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Chrome sandbox - CVE-2014-5332

Posted by Lee Campbell, Graphics Pwning Unit


[This guest post continues Project Zero’s practice of promoting excellence in security research on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Project Zero blog]

Background:

  • Chrome for Android implements a very different sandbox model to that of Chrome for Linux. One of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 platform features we make use of is enabled with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 android:isolatedProcess attribute on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 tag in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 app manifest. The docs say:
android:isolatedProcess
If set to true, this service will run under a special process that is isolated from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 rest of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system and has no permissions of its own. The only communication with it is through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Service API (binding and starting).
  • Under cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 covers this places cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 specified Android Service into a more restrictive SELinux policy and is what Chrome uses for its renderer processes. All is good so far.
  • Back in September I decided to take a look at how ‘isolated’ this isolatedProcess actually is, and specifically look at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel attack surface.
  • As it turns out access to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 GPU drivers is not blocked and this blog is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 story of exploiting Nvidia’s NVMAP driver on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Nexus 9...

What is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 NVMAP driver?

  • It’s used for GPU memory management on Nvidia’s Tegra chipsets. It’s used in many Android devices and more recently Chromebooks.
  • You can find cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 driver code used in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Arm64 Nexus 9 here and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fix is here.
  • Nvidia were very responsive and released an upstream fix within a matter of days—cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y have recently released cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir bulletin CVE-2014-5332.

Some internals:

  • The entry point is /dev/nvmap with rw-rw-rw- permissions making it accessible by all.
  • There are a number of IOCTLs used to control this driver and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y’ll be our focus for exploitation.

NVMAP_IOC_CREATE:

  • This ioctl is used to create handles and returns a file descriptor.
  • Handles are allocated globally and stored in struct nvmap_handle.
struct nvmap_handle {
struct rb_node node; /* entry on global handle tree */
atomic_t ref; /* reference count (i.e., # of duplications) */
atomic_t pin; /* pin count */
u32 flags; /* caching flags */
...


  • Local client references are maintained by this struct:
struct nvmap_handle_ref {
struct nvmap_handle *handle;
struct rb_node node;
atomic_t dupes; /* number of times to free on file close */
atomic_t pin; /* number of times to unpin on free */
};

  • The following code is used to create this structure and allocate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file descriptor.
int nvmap_ioctl_create(struct file *filp, unsigned int cmd, void __user *arg)
{
struct nvmap_create_handle op;
struct nvmap_handle_ref *ref = NULL;
struct nvmap_client *client = filp->private_data;
int err = 0;
int fd = 0;

if (copy_from_user(&op, arg, sizeof(op)))
return -EFAULT;

if (!client)
return -ENODEV;

if (cmd == NVMAP_IOC_CREATE) {
ref = nvmap_create_handle(client, PAGE_ALIGN(op.size));
if (!IS_ERR(ref))
ref->handle->orig_size = op.size;
} else if (cmd == NVMAP_IOC_FROM_ID) {
ref = nvmap_duplicate_handle(client, unmarshal_id(op.id), 0);
} else if (cmd == NVMAP_IOC_FROM_FD) {
ref = nvmap_create_handle_from_fd(client, op.fd);
} else {
return -EINVAL;
}

if (IS_ERR(ref))
return PTR_ERR(ref);

fd = nvmap_create_fd(client, ref->handle);
if (fd < 0)
err = fd;
//POINT A
op.handle = fd;

if (copy_to_user(arg, &op, sizeof(op))) { //POINT B
err = -EFAULT;
nvmap_free_handle(client, __nvmap_ref_to_id(ref));
}

if (err && fd > 0)
sys_close(fd);
return err;
}
  • On successful completion cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 handle->ref count is 2: one reference held by our client, and one reference held by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DMA subsystem that provided cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file descriptor in nvmap_create_fd. This call always returns fd’s starting at 1024, so we can predict cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m.
  • When cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fd returned by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ioctl is closed by user-space cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DMA subsystem releases its reference. When cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fd to /dev/nvmap is closed cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 client releases its reference. Once cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 reference count equals zero cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 handle structure is free’ed.

The bug:

  • If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 copy_to_user at point B were to fail cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n nvmap_free_handle will destroy cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 client reference and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 handle->ref count will drop to 1.
  • sys_close will cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n be called on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fd and this will release cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DMA reference, reducing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 handle->ref count to 0 and freeing it.
  • This is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 correct operation and no resources are leaked.
  • However, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 copy_to_user can be forced to fail by providing a userspace address in Read-Only memory, so cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 client reference will always be free’ed.
  • Unfortunately a race condition exists at point A where a second user thread can dup(1024) and acquire anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r handle to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DMA subsystem. Recall fd’s are allocated predictably starting at 1024. This is an easy race to win!
  • The function will continue to call sys_close and return failure. sys_close in this case will not release cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DMA reference as user-space now has anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r handle.

Ok that’s great, what now?

  • When operating correctly, two references are held to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 handle. Due to triggering cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 race above, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is now only one.
  • This is a nice state for an attacker as userspace now has a reference to a handle that it can free asynchronously at any time, e.g., during anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r ioctl call.
  • To trigger cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 free’ing of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 handle struct, userspace can call close() on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 dup’ed fd.

So now we can free on demand, can we break stuff?

  • This is tricky, we need to win more races. ;-)
  • The ioctl calls take cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fd as a reference to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 handle. In this case cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 dup’ed fd can be used to perform operations on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 handle in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel.
  • Here is an example ioctl:
int nvmap_ioctl_getid(struct file *filp, void __user *arg)
{
struct nvmap_create_handle op;
struct nvmap_handle *h = NULL;
if (copy_from_user(&op, arg, sizeof(op)))
return -EFAULT;
h = unmarshal_user_handle(op.handle); //POINT C
if (!h)
return -EINVAL;
h = nvmap_handle_get(h); //POINT D

if (!h)
return -EPERM;
op.id = marshal_id(h);
nvmap_handle_put(h); //POINT E
return copy_to_user(arg, &op, sizeof(op)) ? -EFAULT : 0;
}

  • Most ioctl’s follow this pattern:
    • unmarshal_user_handle - to convert cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fd to 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 handle
    • nvmap_handle_get - this grabs a reference, incrementing handle->ref
    • Do some work
    • nvmap_handle_put - this drops cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 reference, decrementing handle->ref
  • Although userspace can now asynchronously (in anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r thread) decrement cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 handle->ref count and cause a free by calling close cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is only a small window to do this.
  • At point C cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 handle must be valid and close can not be called before this point (or NULL will be returned)
  • At point D anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r reference is taken on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 handle and calling close will not decrement cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 count to zero, so handle will not be free’ed until point E.
  • So cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 only place corruption can occur is if userspace frees cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 handle between points C and D. This race is much more tricky to win as very little happens between cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se two points.
  • Its made harder as to advance this exploit an attacker not only needs to free cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 handle but also needs to allocate something controllable in its space. As a result most of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ioctls do not provide enough time between unmarshal_user_handle and nvmap_handle_get to set up an exploitable condition.

NVMAP_IOC_RESERVE to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 rescue:
  • There is only one viable ioctl where I could generate enough time to create an exploitable condition. The IOC_RESERVE ioctl can be seen here (lots of checks removed for clarity):
int nvmap_ioctl_cache_maint_list(struct file *filp, void __user *arg,
bool is_reserve_ioctl)
{
struct nvmap_cache_op_list op;
u32 *handle_ptr;
u32 *offset_ptr;
u32 *size_ptr;
struct nvmap_handle **refs;
int i, err = 0;

if (copy_from_user(&op, arg, sizeof(op)))
return -EFAULT;
refs = kcalloc(op.nr, sizeof(*refs), GFP_KERNEL);

if (!refs)
return -ENOMEM;

handle_ptr = (u32 *)(uintptr_t)op.handles;
offset_ptr = (u32 *)(uintptr_t)op.offsets;
size_ptr = (u32 *)(uintptr_t)op.sizes;

for (i = 0; i < op.nr; i++) {
u32 handle;

if (copy_from_user(&handle, &handle_ptr[i], sizeof(handle))) {
err = -EFAULT;
goto free_mem;
}

refs[i] = unmarshal_user_handle(handle); //POINT F
if (!refs[i]) {
err = -EINVAL;
goto free_mem;
}
}
if (is_reserve_ioctl) //POINT G
err = nvmap_reserve_pages(refs, offset_ptr, size_ptr,
 op.nr, op.op);
free_mem:
kfree(refs);
return err;
}
  • In this ioctl unmarshal_user_handle is in a loop (point F), which must be completed before cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 handles are used at point G.
  • The iteration count, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365refore cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 time spent in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 loop, is user-controlled. This can be made fairly large (around 4k) to provide enough time to set up cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attack.
  • ref[0] is set to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 dup’ed fd. The rest of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 refs[] are set to point anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r fully valid fd (created without cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 dup race). They can all be cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same.
  • While cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 loop spins ref[0] can be free’ed in anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r thread at point G. Now we have a stale pointer that is about to be used!
  • nvmap_reserve_pages is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n called and is shown here:
int nvmap_reserve_pages(struct nvmap_handle **handles, u32 *offsets, u32 *sizes,
u32 nr, u32 op)
{
int i;
for (i = 0; i < nr; i++) {
u32 size = sizes[i] ? sizes[i] : handles[i]->size;
u32 offset = sizes[i] ? offsets[i] : 0;

if (op == NVMAP_PAGES_RESERVE)
nvmap_handle_mkreserved(handles[i], offset, size);
else
nvmap_handle_mkunreserved(handles[i],offset, size);
}
if (op == NVMAP_PAGES_RESERVE)
nvmap_zap_handles(handles, offsets, sizes, nr);
return 0;
}
  • By setting op != NVMAP_PAGES_RESERVE nvmap_handle_mkunreserved is called on each handle. This is a NoOp for all handles except handle[0] which has been free’ed.
  • nvmap_handle_mkunreserved is shown here along with its helper functions:


static inline void nvmap_handle_mkunreserved(struct nvmap_handle *h,
    u32 offset, u32 size)
{
nvmap_handle_mk(h, offset, size, nvmap_page_mkunreserved);
}


static inline void nvmap_page_mkunreserved(struct page **page)
{
*page = (struct page *)((unsigned long)*page & ~2UL);
}

static inline void nvmap_handle_mk(struct nvmap_handle *h,
  u32 offset, u32 size,
  void (*fn)(struct page **))
{
int i;
int start_page = PAGE_ALIGN(offset) >> PAGE_SHIFT;
int end_page = (offset + size) >> PAGE_SHIFT;
if (h->heap_pgalloc) {
for (i = start_page; i < end_page; i++)
fn(&h->pgalloc.pages[i]);
}
}

  • This code uses cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer h->pgalloc.pages stored in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 handle struct. This points to an array of pointers. This array is iterated through and bit two of each pointer is cleared.
  • start_page and end_page are user controlled so cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 number of pages can be controlled and easily set to one.
  • As cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 handle has been free’ed cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is a momentary opportunity to allocate attacker controlled data in its place and control h->pgalloc.pages
  • This would allow an attacker to clear bit 2 of any word in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system. A lot of work for a single bit clear but I’ll take it :)

Heap groom:
  • The handle struct is 224 bytes and is allocated with kzalloc. This puts it in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 256-kmalloc heap.
  • An attacker needs to find something that allocates quicky, is around 256 bytes long and has controllable content.
  • Seccomp-bpf provides such a facility in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 form of seccomp-bpf programs (or filters). I picked seccomp as I use it a lot, but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are many ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r things in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel you could pick.
  • Allocating a filter (sized around 256 bytes) at point G will hopefully reallocate over cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 free’ed handle struct.
  • The filter is invalid so it is immediately free’ed by seccomp-bpf  but its contents will remain long enough for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attack to succeed.
  • So now we control cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer to a single bit clear!

What bit to clear?
  • We now need to find a single bit in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel that will provide privilege escalation.
  • Arm64 does not protect its kernel text sections and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y are writable by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel itself. As such cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel can modify its own code. So I looked for a nice instruction to modify.
  • Here is a partial disassembly of setuid (Arm64 on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Nexus 9)
ffffffc0000c3bcc:       528000e0        mov     w0, #0x7
ffffffc0000c3bd0:       f9400821        ldr     x1, [x1,#16]
ffffffc0000c3bd4:       f9424034        ldr     x20, [x1,#1152]
ffffffc0000c3bd8:       97ffcd8f        bl      ffffffc0000b7214
ffffffc0000c3bdc:       53001c00        uxtb    w0, w0 //Point H
ffffffc0000c3be0:       35000280        cbnz    w0, ffffffc0000c3c30//POINT I
ffffffc0000c3be4:       b9400680        ldr     w0, [x20,#4]
ffffffc0000c3be8:       6b0002bf        cmp     w21, w0

and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 C version:
SYSCALL_DEFINE1(setuid, uid_t, uid)
{
// [.....]
retval = -EPERM;
if (nsown_capable(CAP_SETUID)) { //POINT J
new->suid = new->uid = kuid;
if (!uid_eq(kuid, old->uid)) {
retval = set_user(new);
if (retval < 0)
goto error;
}
} else if (!uid_eq(kuid, old->uid) && !uid_eq(kuid, new->suid)) {
goto error;
}
// [....]
return commit_creds(new);
error:
abort_creds(new);
return retval;
}

  • If we can make cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 condition at point J return non-zero cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n it would be as if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 processes has CAP_SETUID and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker could call setuid(0) and elevate to root.
  • The asm code at point H is where this condition is evaluated.
  • uxtb w0,w0 just extends cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 byte value in w0 to a full word and stores it back in w0
  • At point I cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 branch is taken if w0 is non-zero, this is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 branch cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker would like to take to gain privilege.
  • The byte code for uxtb w0,w0 is 0x53001c00. Using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 clear bit primitive from above we can change this word to 0x51001c00.
  • This results in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 new instruction sub w0,w0,#7.
  • Now w0 will contain cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 non-zero value -7 and it will appear as if CAP_SETGID is set.
  • Now we just call setuid(0) and we are root.
  • Easy as that!

Winning cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 race:
  • There are a number of races in this attack and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 probability of success (i.e., cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m all landing perfectly) is quite low. That said, a lose is non-fatal (for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 most part) so an attacker can just keep trying until setuid(0) returns success.
  • On a real system this attack takes many thousands of attempts but usually gains root in less than 10 seconds.


2 comments:

  1. Great work, thank you for making cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IT world a safer place!

    ReplyDelete
  2. Fascinating, thank you for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 clear explanation.

    If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel code were write-protected, would cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 single-bit-clear still allow a root exploit?

    ReplyDelete