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

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

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

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

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
GhostLock-GOT-W29 — HUAWEI MatePad Pro 11 GOT-W29에 대한 CVE-2026-43499 (GhostLock) 연구 | Kitploit
도구/GitHubGitHub/zzzxxxxxxxxxx/ghostlock-got-w29
Privilege EscalationVulnerability AnalysisExploitationReverse EngineeringMobile SecurityBinary Exploitation
GitHubzzzxxxxxxxxxx/ghostlock-got-w29

GhostLock-GOT-W29

HUAWEI MatePad Pro 11 GOT-W29에 대한 CVE-2026-43499 (GhostLock) 연구

저장소 보기
419일 전아직 검토되지 않음

인기

모두 보기 →

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

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2026-43499 (GhostLock) — HUAWEI MatePad Pro 11 GOT-W29

GOT-W29(HarmonyOS 4.0, kernel 4.19.157-perf+)에서 CVE-2026-43499(rtmutex/futex-PI UAF, "GhostLock") 권한 상승 연구.

핵심 결론:

  • 취약점의 쓰기 원시어(Write Primitive)는 실기기에서 검증 성공(KPM 지원으로 커널 sysctl_bootid를 재작성, KASLR slide 유도).
  • 하지만 실제 권한 상승(shell, KPM 없음, RT 없음)은 불가능: 4.19 커널의 pselect 반환 경로가 overlay의 lock 워드를 결정적으로 덮어쓰며, 대체 캐리어가 없음. 이는 커널 스택 지오메트리로 결정된 막다른 길이며, 구현 결함이 아님(비교 대상 smt878u/popsicle은 스택 지오메트리가 달라 완전한 권한 상승 가능).

장치

항목값
모델HUAWEI MatePad Pro 11 GOT-W29
SoCQualcomm kona (SM8250, Snapdragon 870)
시스템HarmonyOS 4.0 (104.0.0.136)
커널4.19.157-perf+
VA39-bit, 4K pages, KASLR on

취약점

kernel/locking/rtmutex.c의 remove_waiter()가 rt_mutex_start_proxy_lock() 롤백 경로에서 waiter->task가 아닌 current로 정리하여 댕글링 pi_blocked_on(스택 UAF)을 유발. 2.6.39 ~ 7.1에 영향(본 커널은 범위 내). 업스트림 수정 커밋 3bfdc63936dd.

본 장치 확인: 소스 rtmutex.c:1110-1112, boot.elf 디컴파일, 실기기 트리거 모두 검증.

트리거

취약점 트리거 메커니즘

PI 링을 만들어 FUTEX_CMP_REQUEUE_PI가 -EDEADLK를 반환하게 하고, 롤백이 remove_waiter 버그를 트리거하여 댕글링 pi_blocked_on(waiter 스레드 커널 스택의 rt_waiter를 가리킴)을 남김.

기존 트리거가 실패한 이유

기존 트리거는 waiter가 requeue 대상 futex를 자체 보유(self-own)하게 하여, 본 커널의 task_blocks_on_rt_mutex 조기 owner==task 검사(boot.elf 0x3808-0x3868)에 정확히 걸려 pi_blocked_on 쓰기 전에 반환 → 댕글링 포인터가 절대 생성되지 않음 → overlay 배치는 오진(크래시 없음 + boot_id 불변).

올바른 트리거(PI 링, 구현 완료)

PI 링: owner가 FUTEX_LOCK_PI(target)으로 requeue 대상을 보유; waiter는 chain futex 보유; owner는 다시 chain에 블록(링: waiter→target→owner→chain→waiter). requeue 시 체인 워크가 rt_mutex_owner(chain)==top_task를 감지 → -EDEADLK → 롤백이 잘못된 대상의 pi_blocked_on을 정리 → waiter의 pi_blocked_on이 댕글링. owner는 우선순위를 낮춰야 함 (nice=10) — boost 후 prio가 owner_waiter->prio와 달라야 하며, 그렇지 않으면 rt_mutex_waiter_equal이 조기 종료.

성과

KASLR 누출(perf_event_open)

shell(uid 2000)에서 perf_event_paranoid=-1, perf_event_open(PERF_SAMPLE_IP, exclude_user=1)으로 커널 텍스트 주소 클러스터를 샘플링, 알려진 심볼 오프셋과 정렬하여 slide 도출.

root@kitploit:~
samples=27651 kernel_ips=1685 lo=0xffffff948728176c hi=0xffffff9488ebfc7c
KASLR slide=0x147f200000    (40/40 IP가 커널 텍스트 영역에 매핑됨을 검증)
runtime _stext=0xffffff9487280800

도구: tools/perf_kaslr.c. 실행 전제: shell(Shizuku rish), seccomp 차단 없음.

EDEADLK 트리거

root@kitploit:~
[M] CMP_REQUEUE_PI ret=-1 errno=35 (EDEADLK!)
[W] WAIT_REQUEUE_PI ret=-1 errno=110 (ETIMEDOUT)  ← waiter 반환
[M] waiter_returned=1                              ← 댕글링 pi_blocked_on 남김

도구: tools/edeadlk_probe.c(variant 8+2+1 = 11, 또는 27).

쓰기 원시어 메커니즘

rt_mutex_adjust_prio_chain step[7]이 fake waiter에 대해 rb_erase(단일 왼쪽 자식 경로) 수행: *(tree_left) = tree_pc + __rb_change_child 증분 쓰기. target.h의 모든 오프셋은 boot.elf 디스어셈블리 실측값.

전체 오프셋

exploit/ghostlock-source/src/target.h 참조. 요점:

  • task_struct: cred=0x988, prio=0x184, pi_blocked_on=0xa90, usage=0x68, mm=0x728
  • rt_mutex_waiter (HW_FUTEX_PI): tree@0x0, pi_tree@0x18, task@0x30, lock@0x38, major@0x40, prio@0x48, deadline@0x50
  • PAGE_OFFSET=0xffffffc000000000, PHYS_OFFSET=0x80000000 (kona), KIMAGE_TEXT_BASE=0xffffff8008080000

쓰기 원시어 실기기 검증

자체 작성 KPM(tools/kpm-debug/rtmutex-dbg.c, KernelPatch 0.13.5 inline-hook)이 fake walk 시 overlay(tree/task/lock)를 재구성하고 next_lock 파라미터를 empty_zero_page로 재작성(KernelPatch _transit8가 수정된 fargs로 원래 함수 호출)하여, rt_mutex_adjust_prio_chain [3] next_lock==waiter->lock 통과, [5] trylock 제로 락 성공, [6] ownerless, [7] rt_mutex_dequeue(rb_erase 단일 왼쪽 자식) 실행 — sysctl_bootid가 &loggers[0][1]로 재작성됨, slide-kaslr-ok.

root@kitploit:~
REPAIR3 waiter=0xffffff801ecdbc00 lock=0xffffffa7c4950000
slide boot_id_leaked_nfulnl_logger value=ffffffa7c4612320
slide-kaslr-ok base=ffffffa7c1280000 slide=00000027b9200000

Overlay 설계

스택 지오메트리는 boot.elf 프레임 크기로 확정: __arm64_sys_futex(0x70) + do_futex(0x60+0x1a0)에서 rt_waiter는 sp+0xc0 → 깊이 0x1b0; pselect 경로 stack_fds[0] 깊이 0x210, 차이 0x60 = 12 words. 따라서 word_i는 stack_fds[12+i]에 위치: words 0-2는 ex[2..4] 입력 영역(직접 제어 가능), words 6-7(task/lock)은 res_in[3..4](in[3..4] + POLLIN-ready fd로 인코딩), words 3-5/8-10은 0 유지. pselect는 ready fd로 즉시 반환 → waiter는 사용자 공간에서 바쁜 대기(시그널 금지, 제로 syscall)하며 consumer가 완료할 때까지 대기.

실제 권한 상승이 불가능한 이유

1. pselect 캐리어의 lock 워드가 반환 경로에서 덮어써짐

overlay의 task/lock words는 res_in[3]/[4](fd ready로 인코딩)에 위치. 실측 res_in[4](lock)는 pselect 반환 경로에서 결정적으로 덮어써짐(rt_sigreturn 프레임 잔여물), payload의 fake_lock과 일치한 적 없음; res_in[3](task)은 가끔 온전함. 사용자 공간 회피 (FP 연산 + sched_yield)는 do_notify_resume 트리거율을 ~21%로 낮출 수 있지만, lock 덮어쓰기는 거의 필연적.

관련 사실 두 가지 추가:

  • ownerless 경로는 4.19에서 차단됨: rt_mutex_adjust_pi()에 if (!owner) return 0;가 있어 fake_lock에 owner가 없으면 adjust_prio_chain을 호출하지 않음. popsicle(6.12)에는 이 검사가 없음. owner-ful payload는 이식됨(fake_lock owner=fake_task|1) 그러나 lock 워드가 반드시 덮어써져 신뢰할 수 없게 트리거됨.
  • empty_zero_page는 강제 쓰기 대상으로 사용 불가: 쓰면 전 시스템 공유 제로 페이지가 손상 → 테스트 후 oops 폭풍.

2. 대체 캐리어(ppoll)는 인코딩 수준에서 불가

boot.elf 디스어셈블리 __arm64_sys_ppoll / do_sys_poll 확인 후: pollfd는 16바이트 구조(fd 4B + events 4B + revents 4B + pad 4B)로 fake waiter의 64비트 task/lock words를 담을 수 없음 — fd 값은 제한적(실제 fd여야 함), events는 4바이트뿐이고 연속적이지 않으며, revents는 커널이 기록(제어 불가).

3. 다른 장치와의 차이

smt878u / popsicle은 완전한 권한 상승 가능: 해당 스택 지오메트리는 words가 사용자 제어 가능한 in/out/ex(pselect 3개 fd_set)에 위치하도록 허용. GOT-W29의 waiter 위치(bits+0x60 → task/lock이 res_in[3]/[4])에는 해당 창이 없음. 이는 커널 스택 지오메트리 차이이며, 구현 결함이 아님.

4. 일반 앱 도메인의 제한

앱 도메인(untrusted_app)에는 기성 KASLR 채널이 없음(perf/kallsyms/pagemap/dmesg 모두 거부됨); CMP_REQUEUE_PI는 1(requeue 성공)을 반환하여 EDEADLK 롤백을 거치지 않음; 비-root cpuset의 major_only는 체인 워크의 step[6]을 하드 쇼트서킷. QOS 변경의 유일한 진입점 /dev/iaware_qos_ctrl은 SELinux에서 거부됨. 따라서 일반 앱 권한으로는 이 CVE를 트리거할 수 없음.

디버깅 도구 체인

자체 작성 KernelPatch 모듈(rtmutex-dbg)로 실기기에서 fake waiter와 쓰기 원시어를 관찰, LyraVoid/KernelPatch 0.13.5(FolkPatch 동일 소스) 헤더 기반 컴파일.

hook 집합

컴파일

root@kitploit:~
cd <KernelPatch>/kpms/rtmutex-dbg
make TARGET_COMPILE=aarch64-linux-android- \
  CC=$PREFIX/bin/aarch64-linux-android-clang \
  LD=$PREFIX/bin/aarch64-linux-android-ld

산출물 rtmutex_dbg.kpm. 컴파일 플래그에 반드시 -fno-pic -fno-pie -fno-asynchronous-unwind-tables -fno-unwind-tables 포함(Makefile에 내장): clang 기본 PIC는 GOT 재배치 생성, 기본 .eh_frame (R_AARCH64_PREL32) 생성 — KPM 로더가 모두 미지원 → 로드 실패 -1.

로드

FolkPatch의 superkey는 su(APatch 기본 KernelPatch 아님). sc_kpm_load 도구 사용(소스 sc_kpm_load.c):

root@kitploit:~
adb shell /data/local/tmp/sc_kpm_load su /sdcard/Download/rtmutex_dbg.kpm  # 로드
adb shell /data/local/tmp/sc_kpm_load unload rtmutex-dbg su               # 언로드
adb shell /data/local/tmp/sc_kpm_load ctl rtmutex-dbg counts su           # 카운터

원버튼 테스트

run_rtmdbg_test.sh(장치 측 /data/local/tmp/ghostlock-test/): shell 신분 (GOT_SLIDE_NO_RT=1 실제 경로)으로 GhostLock 실행, 0.5s sync 루프로 로그 유실 방지, dmesg -w 디스크 기록, 90s 후 자동 수거(SIGSTOP으로 soft-lock 재부팅 방지). 테스트 전:

root@kitploit:~
adb shell 'su -c "sh /sdcard/ghostlock-test/set_debug_no_reboot.sh"'  # 재부팅 방지

관찰 지점(dmesg [RTMDBG])

  • REPAIR3: 쓰기 원시어 검증 시 overlay 재구성 및 next_lock 파라미터 재작성
  • FAKEWALK skip: 덮어쓴 fake walk가 건너뜀(쓰지 않고 크래시 없음)
  • prio_chain[N] / prio_chain_ret: walk 호출 및 반환값(0=완주; 4294967261=-EDEADLK)
  • do_select n=320: 커널 측 res_in[3]/[4]
  • futex op=13/14: CMP_REQUEUE_PI / WAIT_REQUEUE_PI

HUAWEI oops/panic 전체 스택은 /data/log/bbox/history.log에 기록.

디렉터리

root@kitploit:~
tools/      검증 도구(perf KASLR, EDEADLK 프로브, KPM 도구 체인)
target/     전체 실측 오프셋
exploit/    이식된 slide.c(EDEADLK 트리거 변경 포함)

감사

  • 업스트림 PoC: x-spy/CVE-2026-43499-popsicle, soralis0912/CVE-2026-43499-aristotle, JoinChang/ghostlock-oneplus, Wtrwx/smt878u-ionstack-poc (GPL-3.0)
  • CVE: NVD, Red Hat RHSB-2026-010
도구 다운로드
hook역할
rt_mutex_adjust_piPI 조정 기록; overlay 시 skip(pi_blocked_on 정리)
rt_mutex_adjust_prio_chainfake walk 무조건 skip; 전체 waiter 덤프
__arm64_sys_pselect6 / __arm64_sys_ppoll반환 경로에서 _TIF_WORK_MASK 정리; fd_set 관찰
do_select커널 측 res_in[3]/[4] 읽기
__arm64_sys_futexWAIT_REQUEUE_PI / CMP_REQUEUE_PI 추적
rt_mutex_dequeuestep[7] 쓰기 원시어 실행과 tree 형태 확인