Getting root on OnePlus 15 from an untrusted app, via an audio debug service and a vendor HAL
오디오 디버그 서비스와 벤더 HAL을 거쳐 OnePlus 15에서 루트 권한 획득
권한을 요구하지 않는 일반 앱이 OxygenOS의 AtlasService 명령 주입 취약점과 OplusLogCore HAL의 셸 실행 기능을 연결해 uid 0 권한을 얻는 과정을 공개했습니다. 연구자는 OnePlus 12 Pro와 OnePlus 15에서 같은 APK로 공격을 재현했으며, OnePlus는 여러 기기와 소프트웨어 버전이 영향을 받는다고 확인했습니다.
- 주제
AI 요약
연구자는 OnePlus 15(CPH2747, OxygenOS 16) 펌웨어를 살펴보다가 특별한 권한이 없는 일반 앱이 루트 권한까지 도달하는 취약점 두 개를 연결했습니다. 익스플로잇 개발과 역공학은 이미 루팅한 OnePlus 12 Pro에서 진행했고, 완성한 APK를 OnePlus 15에 설치해 수정 없이 재현했습니다. OnePlus는 여러 기기와 소프트웨어 버전이 영향을 받는다고 확인했지만, 전체 영향 범위는 아직 공개하지 않았습니다.
AtlasService에서 명령 실행
OxygenOS의 AtlasService는 루트로 실행되며, Binder 호출을 보내는 프로세스의 권한을 확인하지 않습니다. 연구자가 주목한 setEvent 함수는 이벤트 이름과 값을 받습니다. 특정 이벤트 이름인 atlas_event_multimedia_audio_dumpsys를 보내면 이벤트 처리기가 audioDumpInfo 서비스를 시작합니다. 이 서비스는 init이 uid 0, SELinux 도메인 u:r:dumpstate:s0로 실행합니다.
audioDumpInfo는 공격자가 지정한 시스템 속성 값을 경로에 넣은 뒤, 경로에 포함된 디렉터리를 만들 때 system()으로 chmod 명령을 실행합니다. 입력값을 셸 명령에 넣기 전 이스케이프하지 않아 세미콜론 등을 이용한 명령 주입이 가능합니다. Android 시스템 속성의 값은 92바이트로 제한되지만, 연구자는 외부 저장소에 둔 스크립트를 실행하는 짧은 페이로드를 구성했습니다. 그 결과 일반 앱이 dumpstate 도메인에서 명령을 실행합니다. 이 도메인은 루트 uid를 쓰지만 SELinux와 Linux capability 제약을 받습니다.
벤더 HAL에서 더 넓은 권한으로 실행
두 번째 취약점은 vendor.oplus.hardware.olc2.IOplusLogCore/default 인터페이스의 doShell 함수입니다. 이 함수는 호출자의 uid가 0인지 확인한 뒤, 전달받은 명령을 /vendor/bin/sh -c로 실행합니다. 첫 번째 취약점으로 dumpstate 권한을 얻은 뒤 이 HAL을 호출하면, 자식 프로세스는 SELinux 도메인 u:r:vendor_qti_init_shell:s0에서 실행됩니다. 이 도메인의 CapBnd 값은 0x1ffffffffff로, CAP_SYS_MODULE과 CAP_SYS_RAWIO, CAP_SYS_PTRACE를 비롯한 Linux capability를 모두 포함합니다. SELinux 정책은 dumpstate가 해당 HAL에 Binder 호출을 보내도록 허용합니다.
두 취약점을 연결하면 공격 흐름은 다음과 같습니다. 일반 앱이 AtlasService.setEvent를 호출하고, 입력값을 이용해 audioDumpInfo에서 명령을 실행합니다. 이어 dumpstate 프로세스가 olc2 HAL의 doShell을 호출하면 더 많은 capability를 가진 도메인에서 명령이 실행됩니다. 연구자의 개념 증명 앱은 매니페스트에 특별 권한을 선언하지 않았으며 API 35를 대상으로 합니다.
APK 실행과 Binder 호출 구현
연구자는 설치된 APK를 CLASSPATH에 직접 지정하는 방법을 시도했지만, APK 파일의 SELinux 라벨 때문에 dumpstate가 읽지 못했습니다. 대신 앱의 외부 파일 디렉터리에 classes.dex를 추출하고, app_process로 실행하는 스크립트를 만들었습니다. 외부 파일 디렉터리는 dumpstate가 읽을 수 있는 라벨을 사용합니다.
Java 코드에서 vendor HAL을 직접 호출하는 공식 경로는 없었지만, ServiceManager.getService로 Binder 서비스를 찾고 Parcel에 인터페이스 토큰과 인자를 기록한 뒤 transact를 호출해 doShell을 실행했습니다. Stable AIDL의 문자열은 UTF-16 형식이므로 Parcel.writeString을 그대로 사용했습니다. 반면 AtlasService는 구형 String8 형식을 사용해 Java Parcel에서 바이트 배열을 직접 조립해야 했습니다.
수정 제안과 공개 경과
AtlasService에는 호출자의 uid와 SELinux 도메인을 확인하는 검사가 필요하며, audioDumpInfo는 신뢰할 수 없는 입력을 system()에 넘기지 않아야 한다고 연구자는 제안합니다. olc2 HAL의 doShell을 제거하거나, 호출 가능한 프로세스를 특정 디버그 데몬으로 제한하는 방안도 제시했습니다.
연구자는 2026년 4월 18일 OnePlus 보안팀에 취약점을 처음 보고했습니다. OnePlus는 5월 20일 여러 제품에서 취약점이 확인됐다고 답하면서 공개 시 법적 조치를 언급했습니다. 이후 연구자는 공개 일정을 통보했고, 6월 22일 OnePlus는 수정 작업을 이유로 공개 연기를 요청했습니다. 연구자는 9월 17일 이후 공개하겠다고 답했으며, 글은 9월 24일 게시됐습니다.
Reddit 반응
- @nns_ee — /u/nns_ee가 제출했습니다. [링크] [댓글]
원문: nns.ee 블로그 / 번역·요약: Trawling